diff --git a/devdocs/specs/core-notification.txt b/devdocs/specs/core-notification.txt index 9a285d0f..b41e9a2f 100644 --- a/devdocs/specs/core-notification.txt +++ b/devdocs/specs/core-notification.txt @@ -66,8 +66,8 @@ TODO: Translation keys needed: NotifyDeliveryMethod NotifyEventType NotifyDeliveryAddress - InTags - OutTags + tags + todo: updating subscriptions must clear out old events and notifications if they are affected @@ -109,9 +109,9 @@ OBJECTS Saving an object triggers notification processing for that object: DUPES - there can be near dupes here because user may have multiple of identical but vary in only: Delivery method advancenoticetimespan (e.g. contract expiring could be one 90 days in advance to renegotiate and then one 10 days in advance for notify service to cancel etc) - InTags - notify applies if has these tags + tags - notify applies if has these tags empty means any - OutTags - notify cancelled if has these tags + s - notify cancelled if has these tags empty means any out trumps an in, default is safer option @@ -186,11 +186,11 @@ MailMessageDelivery /notifysubscription table - contains all user (and customer user) subscriptions to events and delivery method, if user wants two delivery methods they have two entries here, no other table indicates events to be tracked, has user id - also ayatype GLOBAL means that the subscription applies to events of any corebiz type - so for example a CreatedWithTag notification or UpdatedWithTag notification will work on any object with that tag if global or a specific type i.e. customer with that tag - Then they can control by tag as well, intags are used to filter and include (or any if no intags), outtags filter out and trump intags + also ayatype must be specific not GLOBAL to avoid a "sorcerers apprentice" runaway issue and many role related issues potentially + so for example a CreatedWithTag notification or UpdatedWithTag notification will work on any object with that tag if a specific type i.e. customer with that tag + Then they can control by tag as well, tags are used to filter and include (or any if no tags), s filter out and trump tags These are also used for tag type conditions - (aID, userId, ayatype, aEventType, advancenoticetimespan, agevalue, idvalue, decvalue, aDeliveryMethod, deliveryaddress, attachreportid, intags, outtags, HASH) + (aID, userId, ayatype, aEventType, advancenoticetimespan, agevalue, idvalue, decvalue, aDeliveryMethod, deliveryaddress, attachreportid, tags, s, HASH) /Notifyevent table - contains all events, created by updating objects or directly in some cases and used by generator for processing as deliveries created (timestamp used to clean out old stuck events), ayatype, objectid, eventtype, appliestouserid (single subscriber event), aEventDate, ASAVEDMESSAGE, HASH? @@ -261,13 +261,16 @@ UnitMeterReadingMultipleExceeded case 1254 [GENERAL] DefaultNotification case 3792 used for all system and old Quick notifications [EVERYONE] Always present for inapp, user can add more for email if they want and filters or whatever but there is always a built in non-visible subscription to inapp for this -deprecated, use objectCUD events for this: TagNotification case 3799 [CONDITION: intag, outtag, ayatype global/specific] +deprecated, use objectCUD events for this: TagNotification case 3799 [CONDITION: tag, , ayatype global/specific] (Note: this is actually just general notification if an object is created or updated and the tagging is optional) CRUD Notifications: - (note: tag filterable and also can select type specifically or global for any type) - (note: differs from tag notification above in that only applies on a CHANGE of TAGS, this is an always for this op and tags are just an optional filter) - [CONDITION: intag, outtag, ayatype global/specific] + (note: tag filterable and select type specifically. Global is not a valid type for this to avoid issues) + (note: + + ### WHAT DOES THIS MEAN?:differs from tag notification above in that only applies on a CHANGE of TAGS, this is an always for this op and tags are just an optional filter) + + [CONDITION: tag, , ayatype specific] ObjectCreated ObjectUpdated ObjectDeleted diff --git a/server/AyaNova/DataList/NotifySubscriptionDataList.cs b/server/AyaNova/DataList/NotifySubscriptionDataList.cs index 1224290f..c742d6ff 100644 --- a/server/AyaNova/DataList/NotifySubscriptionDataList.cs +++ b/server/AyaNova/DataList/NotifySubscriptionDataList.cs @@ -37,12 +37,10 @@ namespace AyaNova.DataList dlistView.Add(cm); cm = new JObject(); - cm.fld = "intags"; + cm.fld = "tags"; dlistView.Add(cm); - cm = new JObject(); - cm.fld = "outtags"; - dlistView.Add(cm); + DefaultListView = dlistView.ToString(Newtonsoft.Json.Formatting.None); @@ -104,16 +102,11 @@ namespace AyaNova.DataList FieldDefinitions.Add(new AyaDataListFieldDefinition { TKey = "InTags", - FieldKey = "intags", + FieldKey = "tags", UiFieldDataType = (int)UiFieldDataType.Tags }); - FieldDefinitions.Add(new AyaDataListFieldDefinition - { - TKey = "OutTags", - FieldKey = "outtags", - UiFieldDataType = (int)UiFieldDataType.Tags - }); + } diff --git a/server/AyaNova/biz/NotifyEventProcessor.cs b/server/AyaNova/biz/NotifyEventProcessor.cs index eedd152c..bae51b60 100644 --- a/server/AyaNova/biz/NotifyEventProcessor.cs +++ b/server/AyaNova/biz/NotifyEventProcessor.cs @@ -37,22 +37,30 @@ namespace AyaNova.Biz await AddEvent(new NotifyEvent() { EventType = NotifyEventType.ServerOperationsProblem, Message = message }); } - - public static async Task AddEvent(NotifyEvent ev) + //Add event if there are subscribers + public static async Task AddEvent(NotifyEvent ev, List inTags = null) { log.LogTrace($"AddEvent processing: [{ev.ToString()}]"); try { - - log.LogTrace("Notify set to RUNNING state and starting now"); + List subs = new List(); using (AyContext ct = AyaNova.Util.ServiceProviderProvider.DBContext) { //iterate subs, figure out who gets this event //add to table for any that do - var subs=await ct.NotifySubscription.AsNoTracking().Where(z=>z.EventType==ev.EventType &&) + if (inTags == null || inTags.Count==0) + { + //No tags + subs = await ct.NotifySubscription.AsNoTracking().Where(z => z.EventType == ev.EventType && z.AyaType == ev.AyaType).ToListAsync(); + } + + else + { + //In tags + } } } diff --git a/server/AyaNova/biz/NotifySubscriptionBiz.cs b/server/AyaNova/biz/NotifySubscriptionBiz.cs index 46abd0bb..bc9ab9f3 100644 --- a/server/AyaNova/biz/NotifySubscriptionBiz.cs +++ b/server/AyaNova/biz/NotifySubscriptionBiz.cs @@ -47,14 +47,12 @@ namespace AyaNova.Biz return null; else { - newObject.InTags = TagBiz.NormalizeTags(newObject.InTags); - newObject.OutTags = TagBiz.NormalizeTags(newObject.OutTags); + newObject.Tags = TagBiz.NormalizeTags(newObject.Tags); await ct.NotifySubscription.AddAsync(newObject); await ct.SaveChangesAsync(); await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct); - await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.InTags, null); - await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.OutTags, null); + await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); return newObject; } } @@ -80,8 +78,7 @@ namespace AyaNova.Biz await ct.SaveChangesAsync(); await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct); //await SearchIndexAsync(newObject, true); - await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.InTags, null); - await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.OutTags, null); + await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); return newObject; } @@ -112,8 +109,7 @@ namespace AyaNova.Biz NotifySubscription SnapshotOfOriginalDBObj = new NotifySubscription(); CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj); CopyObject.Copy(putObject, dbObject, "Id");//can update serial - dbObject.InTags = TagBiz.NormalizeTags(dbObject.InTags); - dbObject.OutTags = TagBiz.NormalizeTags(dbObject.OutTags); + dbObject.Tags = TagBiz.NormalizeTags(dbObject.Tags); ct.Entry(dbObject).OriginalValues["Concurrency"] = putObject.Concurrency; await ValidateAsync(dbObject); @@ -131,8 +127,7 @@ namespace AyaNova.Biz return null; } await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct); - await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.InTags, SnapshotOfOriginalDBObj.InTags); - await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.OutTags, SnapshotOfOriginalDBObj.OutTags); + await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags); return dbObject; } @@ -159,8 +154,7 @@ namespace AyaNova.Biz //Log event await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, dbObject.EventType.ToString(), ct); // await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType, ct); - await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.InTags); - await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.OutTags); + await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); //await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct); //TODO: DELETE RELATED RECORDS HERE //all good do the commit diff --git a/server/AyaNova/models/NotifySubscription.cs b/server/AyaNova/models/NotifySubscription.cs index d1ddda57..defb97f4 100644 --- a/server/AyaNova/models/NotifySubscription.cs +++ b/server/AyaNova/models/NotifySubscription.cs @@ -15,10 +15,7 @@ namespace AyaNova.Models public uint Concurrency { get; set; } [Required] - public long UserId { get; set; } - public AyaType? AyaType { get; set; }//Note: could be Global meaning any corebiz object would be included if relevant (e.g. ObjectCreated) - [Required] - public NotifyEventType EventType { get; set; } + public long UserId { get; set; } public TimeSpan? AdvanceNotice { get; set; } //Note: I've been doing nullable wrong sort of: https://stackoverflow.com/a/29149207/8939 public long? IdValue { get; set; } public decimal? DecValue { get; set; } @@ -27,13 +24,18 @@ namespace AyaNova.Models public NotifyDeliveryMethod DeliveryMethod { get; set; } public string DeliveryAddress { get; set; } public long? AttachReportId { get; set; } - public List InTags { get; set; } - public List OutTags { get; set; } + + //CONDITIONS - Following fields are all conditions set on whether to notify or not + public AyaType AyaType { get; set; }//Note: must be specific object, not global for any object related stuff to avoid many role issues and also potential overload + [Required] + public NotifyEventType EventType { get; set; } + public List Tags { get; set; }//Tags to filter an event, object *must* have these tags to generate event related to it (AT TIME OF UPDATE) + public NotifySubscription() { - InTags = new List(); - OutTags = new List(); + Tags = new List(); + AyaType= AyaType.NoType; } }//eoc diff --git a/server/AyaNova/resource/de.json b/server/AyaNova/resource/de.json index d77211f4..9476ea30 100644 --- a/server/AyaNova/resource/de.json +++ b/server/AyaNova/resource/de.json @@ -1857,7 +1857,6 @@ "NotifyEventType": "Benachrichtigungsereignis", "NotifyDeliveryAddress": "An Adresse liefern", "InTags": "Filtern Sie in diesen Tags", - "OutTags": "Filtern Sie diese Tags heraus", "NotifyEventObjectDeleted": "Objekt gelöscht", "NotifyEventObjectCreated": "Objekt erstellt", "NotifyEventObjectModified": "Objekt geändert", diff --git a/server/AyaNova/resource/en.json b/server/AyaNova/resource/en.json index 39fd3681..a58a60af 100644 --- a/server/AyaNova/resource/en.json +++ b/server/AyaNova/resource/en.json @@ -1857,7 +1857,6 @@ "NotifyEventType": "Notification event", "NotifyDeliveryAddress": "Deliver to address", "InTags": "Filter include tags", - "OutTags": "Filter exclude tags", "NotifyEventObjectDeleted": "Object deleted", "NotifyEventObjectCreated": "Object created", "NotifyEventObjectModified": "Object changed", diff --git a/server/AyaNova/resource/es.json b/server/AyaNova/resource/es.json index 7ff020e9..6e809b4c 100644 --- a/server/AyaNova/resource/es.json +++ b/server/AyaNova/resource/es.json @@ -1857,7 +1857,6 @@ "NotifyEventType": "Evento de notificación", "NotifyDeliveryAddress": "Entregar a la dirección de correo electrónico", "InTags": "Filtrar incluir etiquetas", - "OutTags": "Filtrar etiquetas excluidas", "NotifyEventObjectDeleted": "Objeto eliminado", "NotifyEventObjectCreated": "Objeto creado", "NotifyEventObjectModified": "Objeto cambiado", diff --git a/server/AyaNova/resource/fr.json b/server/AyaNova/resource/fr.json index 4296597b..e6f36ab3 100644 --- a/server/AyaNova/resource/fr.json +++ b/server/AyaNova/resource/fr.json @@ -1857,7 +1857,6 @@ "NotifyEventType": "Événement de notification", "NotifyDeliveryAddress": "Livrer à l'adresse", "InTags": "Filtrer les balises inclusives", - "OutTags": "Filtrer les tags exclusifs", "NotifyEventObjectDeleted": "Objet supprimé", "NotifyEventObjectCreated": "Objet créé", "NotifyEventObjectModified": "Objet changé", diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index 7b3d7e40..631c0f35 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -684,8 +684,8 @@ $BODY$; LogUpdateMessage(log); await ExecQueryAsync("CREATE TABLE anotifysubscription (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, " + - "userid bigint not null, ayatype integer, eventtype integer not null, advancenotice interval, " + - "idvalue bigint, decvalue decimal(19,4), agevalue interval, deliverymethod integer not null, deliveryaddress text, attachreportid bigint, intags varchar(255) ARRAY, outtags varchar(255) ARRAY)"); + "userid bigint not null, ayatype integer not null, eventtype integer not null, advancenotice interval, " + + "idvalue bigint, decvalue decimal(19,4), agevalue interval, deliverymethod integer not null, deliveryaddress text, attachreportid bigint, tags varchar(255) ARRAY)"); await ExecQueryAsync("CREATE TABLE anotifyevent (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, created timestamp not null, " + "ayatype integer, objectid bigint, eventtype integer not null, subscriptionid bigint not null, idvalue bigint, decvalue decimal(19,4), eventdate timestamp, deliverdate timestamp, message text)");