Service - Notification Manager: Difference between revisions

From Izara Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
[[Service - Notification Manager]]
= Overview =


= Lambda Functions =
Consolidates and sends notifications to any number of receiver services. Will be used for user notifications but can have other types of receiver services added. Activities are received from [[Service - Activity Switchboard|Activity Switchboard]] service.


== RcvActivityMsg ==
Notification groups set whether activities are considated (eg per day/per month/per x number of activities), if considated activities are stored here until sending is triggered, according to the considation rules. If not consolidated they are sent on immediately.


<syntaxhighlight lang="JavaScript">
= Repository =
/**
* Receives one activity message from Activity Switchboard, find matching notification, stores for consolidation or sends immediately
* @param {Object[]} event.activityMsg
* @param {Object} event.activityMsg.messageAttributes
* @param {Object} event.activityMsg.message
* @param {String} event.uniqueId - In NotificationMgr, uniqueId is notificationId
* @param {Object[]} event.additionalData - additional params set in the trigger group
* @param {number} event.time
* @param {string} event.additionalData.notificationId - matches notificationId in notifications table
*
*/
</syntaxhighlight>
 
=== HdrSqs ===
 
Subscribes to [[Service - Activity Switchboard]] OutTriggerGroupPassed topic, (? filter policy limit) receiverTag = {this service's izServiceName}


=== logic ===
https://bitbucket.org/stb_working/notification-manager/src/master/
# get notification node by getNodeAndRelationship
#* nodeLabel: "notification"
#* nodeProperties.notificationId
#* relationshipType: "has_notification"
#* relationshipDirection: "incoming"
#* toNodeVersionedDataLabels: ["notificationGroupSettings"]
# if not found notification or notificationGroup node
#* throw new NoRetryError
# If have setting and consolidated = true
#* save ''activityMsg'' to ConsolidationPending table
# else
#* invoke [[#sendNotificationToReceiver]]


= DynamoDB tables =


== DisableNotificationGroups ==
== PendingConsolidation Table ==


<syntaxhighlight lang="JavaScript">
; notificationGroupId (partition key)
/**
: type: string
* Disables a set of matching notification groups
: comes from: random UUID
* @param {string} notificationGroupId - null if not supplied
; time (sort key)
*
: type: number
*/
: {timestamp activity handled in Activity Switchboard}_{small random UUID}
</syntaxhighlight>
; activityMsg
: type: object
: object containing ''messageAttributes'' and ''message''


=== HdrSqs ===
= Object Schemas =


Subscribes to InDisableNotificationGroups topic
; Additional Information: [[Per Service Schemas]]


=== logic ===
== objType ==


# get notificationGroup node by GraphHandler getNodeAndRelationships
=== notificationGroup ===
#* nodeLabel: "notificationGroup"
#* nodeProperties.notificationGroupId
#* relationshipType: "has_notification"
#* relationshipDirection: "outgoing"
# if not found notificationGroup or notification node
#* throw new NoRetryError
# else
#* get notificationId for each child notification node
# invoke [[#disableNotificationGroup]]


== UpdateNotificationGroup ==
* Groups many notificatons together
* Simplifies management of similar notifications
* Allows for consolidation of notifications at notification group level


<syntaxhighlight lang="JavaScript">
<syntaxhighlight lang="JavaScript">
/**
{
* Update a notification group
objectType: "notificationGroup",
* @param {string} notificationGroupId
canDelete: false,
* @param {string} notificationGroupName
    complexFilterServiceTag: "complexFilter",
* @param {boolean} consolidated
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function
* @param {string} consolidatedType
create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']
* @param {..?} consolidatedFrequency
update: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']
* @param {boolean} consolidatedSendIfEmpty
get: [], // default: ['hdrApi', 'hdrInv']
* @param {boolean} enabled
delete: [], // default: ['hdrApi', 'hdrSqs']
*
},
*/
addOnDataStructure: [
{
type: "versionedData",
versionedDataLabel: "notificationGroupSettings",
storageResourceTag: "myGraph",
fieldNames: [
{ // fieldName in versionedData should now same in main objectSchema
fieldName: "notificationGroupName",
type: "string",
optionalOnCreate: true, // default = false
canUpdate: true, // default = true
validation: {
pattern: pattern
}
},
{
fieldName: "consolidated",
type: "boolean",
optionalOnCreate: true,
canUpdate: true,
validation: {
pattern: pattern
}
},
{
fieldName: "consolidatedType",
type: "string",
optionalOnCreate: true,
canUpdate: true,
validation: {
pattern: pattern
}
},
{
fieldName: "consolidatedFrequency",
type: "string",
optionalOnCreate: true,
canUpdate: true,
validation: {
pattern: pattern
}
},
{
fieldName: "consolidatedNextSendDue",
type: "number",
optionalOnCreate: true,
canUpdate: true,
validation: {
pattern: pattern
}
},
{
fieldName: "consolidatedSendIfEmpty",
type: "boolean",
optionalOnCreate: true,
canUpdate: true,
validation: {
pattern: pattern
}
}
]
}
    ],
    // overwriteGeneratedMainFunction: ["update"],
    storageResources: {
myGraph: {
storageType: "graph",
graphServerTag: "GraphHandler"
}
    },
    fieldNames: { // see Per Service Schemas
notificationGroupId: {
    type: "string",
optionalOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
},
receiverTag: {
type: "string",
requiredOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
},
userId: {
type: "string",
requiredOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
}
    },
    identifiers: [
{
type: "identifier",
fieldName: "notificationGroupId"
}
    ]
}
</syntaxhighlight>
</syntaxhighlight>


=== HdrSqs ===
==== fieldNames ====
 
Subscribes to InUpdateNotificationGroup topic


=== logic ===
; notificationGroupId (identifier)
: comes from: random UUID
; notificationGroupName
: string name set by reciever service, optional
; receiverTag
: set by the creating service name
: eg ContactMethodEmail
; consolidated
: true|false
; consolidatedType
: overview|detailed
: ''overview'' sends a count of each type of notification, ''detailed'' lists each activity message
; consolidatedFrequency
: not sure, maybe use some sort of standard like cron
; consolidatedNextSendDue
: timestamp for next due time to send notification
; consolidatedSendIfEmpty
: true|false
; userId
: for createdBy relationship


# get notificationGroup node by GraphHandler getNodeAndRelationships
#* nodeLabel: "notificationGroup"
#* nodeProperties.notificationGroupId
#* relationshipType: "has_notification"
#* relationshipDirection: "outgoing"
# if not found notificationGroup or notification node
#* throw new NoRetryError
# else
#* get notificationId for each child notification node
# update notificationGroup node by send message to [[Service - Graph Handler]] InCreateVersionedData
#* nodeStructure
#** nodeLabel: "notificationGroup"
#** nodeProperties.notificationGroupId
#* versionDataStructure
#** nodeLabel: "notificationGroupSetting"
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedSendIfEmpty}
# if enabled = false
## invoke [[#disableNotificationGroup]]
# if enabled = true
## invoke [[#enableNotificationGroup]]


== DisableNotifications ==
=== notification ===


<syntaxhighlight lang="JavaScript">
<syntaxhighlight lang="JavaScript">
/**
{
* Disables a set of matching notification groups
objectType: "notification",
* @param {string[]} notificationIds
canDelete: false,
*
complexFilterServiceTag: "complexFilter",
*/
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function
create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']
update: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']
get: [], // default: ['hdrApi', 'hdrInv']
delete: [], // default: ['hdrApi', 'hdrSqs']
},
addOnDataStructure: [
{
type: "versionedData",
versionedDataLabel: "notificationSettings",
storageResourceTag: "myGraph",
fieldNames: [
{ // fieldName in versionedData should now same in main objectSchema
fieldName: "notificationName",
type: "string",
optionalOnCreate: true, // default = false
canUpdate: true, // default = true
validation: {
pattern: pattern
}
}
]
}
    ],
  // overwriteGeneratedMainFunction: ["update"],
    storageResources: {
myGraph: {
storageType: "graph",
graphServerTag: "GraphHandler"
}
    },
    fieldNames: {
notificationId: {
    type: "string",
optionalOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
},
userId: {
type: "string",
requiredOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
}
    },
    identifiers: [
{
type: "identifier",
fieldName: "notificationId"
}
    ]
}
</syntaxhighlight>
</syntaxhighlight>


=== HdrSqs ===
==== fieldNames ====
 
Subscribes to InDisableNotifications topic


=== logic ===
; notificationId (identifier)
: comes from: random UUID
; notificationName
: string name set by reciever service, optional
; userId
: for createdBy relationship


# for each notificationIds
## change relationshipType to "disabled_notification" by send message to [[Service - Graph Handler]] InChangeRelationshipType
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup
##* notificationId
## send message to OutNotificationDisabled topic
##* notificationId


== CreateNotificationGroup ==
=== notificationTrigger ===


<syntaxhighlight lang="JavaScript">
<syntaxhighlight lang="JavaScript">
/**
{
* Creates one notification group
objectType: "notificationTrigger",
* @param {string} notificationGroupId
canDelete: false,
* @param {string} notificationGroupName
    complexFilterServiceTag: "complexFilter",
* @param {boolean} consolidated
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function
* @param {string} consolidatedType
create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']
* @param {..?} consolidatedFrequency
update: [], // default: ['hdrApi', 'hdrSqs']
* @param {boolean} consolidatedSendIfEmpty
get: [], // default: ['hdrApi', 'hdrInv']
* @param {Object} additionalData
delete: [], // default: ['hdrApi', 'hdrSqs']
*
},
*/
    // overwriteGeneratedMainFunction: ["create"],
    storageResources: {
myGraph: {
storageType: "graph",
graphServerTag: "GraphHandler"
}
    },
    fieldNames: {
notificationTriggerId: {
type: "string",
optionalOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
},
propertyName: {
type: "string",
optionalOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
},
valueType: {
type: "string",
requiredOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
},
valueString: {
type: "string",
optionalOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
},
valueNumber: {
type: "number",
optionalOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
},
values: {
type: "arrayMixed",
optionalOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
},
userId: {
type: "string",
requiredOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['myGraph']
}
    },
    identifiers: [
{
type: "identifier",
fieldName: "notificationTriggerId"
}
    ]
}
</syntaxhighlight>
</syntaxhighlight>


=== HdrSqs ===
==== fieldNames ====


Subscribes to InCreateNotificationGroup topic
; notificationTriggerId (identifier)
: comes from: random UUID
; propertyName
: can be a nested property, use dot notation
: "serviceName" and "topicName" valueTypes do not have propertyName
; valueType
: property|attribute|serviceName|topicName
; valueString
: type: string
; valueNumber
: type: number
; values
: type: arrayMixed
; userId
: for createdBy relationship


Validator:
* notificationGroupId cannot be empty string
   
=== logic ===


# if ''consolidated'' = true, validate:
=== consolidated ===
## consolidatedType must equal "overview"|"detailed"
## consolidatedFrequency ..?
## consolidatedSendIfEmpty, set to false if not boolean and set to true
# create notificationGroup by send message to [[Service - Graph Handler]] InCreateNodeWithVersionedData
#* nodeStructure
#** nodeLabel: "notificationGroup"
#** nodeProperties: {notificationGroupId, additionalData}
#* versionedDatas
#** nodeLabel: "notificationGroupSetting"
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedSendIfEmpty}
# send message to OutNotificationGroupCreated topic, can include full notificationGroup object
 
== CreateNotifications ==


<syntaxhighlight lang="JavaScript">
<syntaxhighlight lang="JavaScript">
/**
{
* Creates muti notifications
objectType: "consolidated",
* @param {string} userId
canDelete: false,
* @param {string} notificationGroupId
    complexFilterServiceTag: "complexFilter",
* @param {Object[]} notifications
    storageResources: {
* @param {string} [notifications.notificationName]
dynamoDB: {
* @param {Object[]} notifications.triggers
storageType: "dynamoDB",
* @param {string} notifications.triggers.propertyName
tableName: "PendingConsolidation"
* @param {string} notifications.triggers.valueType - property|attribute
}
* @param {string} [notifications.triggers.value] - either value or values will be set
    },
* @param {string[]} [notifications.triggers.values] - either value or values will be set
    fieldNames: {
*
notificationGroupId: {
*/
type: "string",
requiredOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['dynamoDB'],
fromServiceNameTag: "NotificationMgr" ,
fromObjectType: "notificationGroup"
},
time: {
type: "string",
requiredOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['dynamoDB']
},
activityMsg: {
type: "object",
requiredOnCreate: true,
canUpdate: false,
validation: {
pattern: pattern
},
storageResourceTags: ['dynamoDB']
}
    },
    identifiers: [
{
type: "partitionKey",
fieldName: "notificationGroupId"
},
{
type: "sortKey",
fieldName: "time"
}
    ]
}
</syntaxhighlight>
</syntaxhighlight>


=== HdrSqs ===
==== fieldNames ====


Subscribes to InCreateNotifications topic
; notificationGroupId (partition key)
: comes from: random UUID
; time (sort key)
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId
; activityMsg
: copy of the message delivered from Activity Switchboard


Validator:
* notificationGroupId cannot be empty string
* notifications cannot be an empty array? Can limit number of notifications in array?
   
=== logic ===


# get notificationGroup node by invoke graphSharedLib.getNode
== Object Relationships ==
#* nodeStructure
#** nodeLabel: "notificationGroup"
#** nodeProperties: {notificationId}
#* versionedDataLabels: ["notificationGroupSetting"]
# if not found notificationGroup node
## send message to OutCreateNotificationsFailed topic
##* notificationGroupId
##* notifications
##* errorsFound: "NotificationGroupNotFound"
## return
# for each ''notifications'':
## create an object that contains
##* notificationName
##* triggers
## hash this object into variable named ''notificationId''
## create child notification node and relationship
### send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship
###* parentNodeStructure
###** nodeLabel: "notificationGroup"
###** nodeProperties: {notificationGroupId}
###* createChildNodeStructure
###** nodeStructure
###*** nodeLabel: "notification"
###*** nodePropertis: {notificationId}
###* createRelationshipStructure
###** relationshipStructure
###*** relationshipType: "has_notification"
###*** relationshipDirection: "outgoing"
## for each ''notifications.triggers'':
### create ''notificationTriggerId'' by {notificationId} + "_" + {randon UUID}
### create child notificationTrigger node and relationship
#### send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship
####* parentNodeStructure
####** nodeLabel: "notification"
####** nodeProperties: {notificationId}
####* createChildNodeStructure
####** nodeStructure
####*** nodeLabel: "notificationTrigger"
####*** nodePropertis: {notificationTriggerId}
####* createRelationshipStructure
####** relationshipStructure
####*** relationshipType: "has_notificationTrigger"
####*** relationshipDirection: "outgoing"
## create an object that contains 'notificationId' and 'triggers' for invoke sharedCreateTriggerGroup.sharedCreateTriggerGroup
## invoke sharedCreateTriggerGroup.sharedCreateTriggerGroup
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup topic
###* uniqueId: {notificationId}
###* additionalData: {}
###* triggers: {notification.triggers}
## send message to OutNotificationCreated topic, can include full notification object


== NotificationGroup/List ==
=== has_notificationGroup ===
<syntaxhighlight lang="JavaScript">
{
"has_notificationGroup": {
properties: {
"originTimestamp": {
    type: "number",            // "string" | "number" ...
requiredOnCreate: true,  // default=false
canUpdate: true,          // default = true
validation: {}            // ajv syntax
}
},
storageResources: {
myGraph: {
storageType: "graph",
graphServerTag: "graphHandler"
}
},
links: [
{
storageResourceTags: ["myGraph"],
from: {
objType: {
serviceTag: "UserContactManager",
objectType: "userContact"
},
linkType: "one",
},
to: {
objType: {
serviceTag: "NotificationManager",
objectType: "notificationGroup"
},
requiredOnCreate: true,
linkType: "many",
handler: true
}
}
]
}
}
</syntaxhighlight>
: links userContact to notificationGroup


=== disabled_notificationGroup ===
<syntaxhighlight lang="JavaScript">
<syntaxhighlight lang="JavaScript">
/**
{
* Lists all notification groups for one primary key (notificationGroupId/uniqueId)
"disabled_notificationGroup": {
* @param {string} receiverTag
properties: {
* @param {string} groupingId
"originTimestamp": {
* @param {string} [uniqueIdPrefix=null]
    type: "number",            // "string" | "number" ...
*
requiredOnCreate: true,  // default=false
* @returns {Object[]} array of objects, each object is one notification group
canUpdate: true,          // default = true
*/
validation: {}           // ajv syntax
}
},
storageResources: {
myGraph: {
storageType: "graph",
graphServerTag: "GraphHandler"
}
},
links: [
{
storageResourceTags: ["myGraph"],
from: {
objType: {
serviceTag: "UserContactManager",
objectType: "userContact"
},
linkType: "one"
},
to: {
objType: {
serviceTag: "NotificationManager",
objectType: "notificationGroup"
},
linkType: "many",
handler: true
}
}
]
}
}
</syntaxhighlight>
</syntaxhighlight>


=== HdrApi ===


Validator:
=== has_notification ===
* receiverTag
<syntaxhighlight lang="JavaScript">
* groupingId
{
* uniqueIdPrefix with default value
"has_notification": {
properties: {
"originTimestamp": {
    type: "number",            // "string" | "number" ...
requiredOnCreate: true,  // default=false
canUpdate: true,          // default = true
validation: {}            // ajv syntax
}
},
storageResources: {
myGraph: {
storageType: "graph",
graphServerTag: "GraphHandler"
}
},
links: [
{
storageResourceTags: ["myGraph"],
from: {
objType: {
serviceTag: "NotificationManager",
objectType: "notificationGroup"
},
linkType: "one",
handler: true
},
to: {
objType: {
serviceTag: "NotificationManager",
objectType: "notification"
},
requiredOnCreate: true,
linkType: "many",
handler: true
}
}
]
}
}
</syntaxhighlight>


==== handler logic ====


(standard)
=== disabled_notification ===
<syntaxhighlight lang="JavaScript">
{
"disabled_notification": {
properties: {
"originTimestamp": {
    type: "number",            // "string" | "number" ...
requiredOnCreate: true,  // default=false
canUpdate: true,          // default = true
validation: {}            // ajv syntax
}
},
storageResources: {
myGraph: {
storageType: "graph",
graphServerTag: "graphHandler"
}
},
links: [
{
storageResourceTags: ["myGraph"],
from: {
objType: {
serviceTag: "NotificationManager",
objectType: "notificationGroup"
},
linkType: "one",
handler: true
},
to: {
objType: {
serviceTag: "NotificationManager",
objectType: "notification"
},
linkType: "many",
handler: true
}
}
]
}
}
</syntaxhighlight>


=== logic ===


Query NotificationGroups table for all records that match notificationGroupId and if uniqueIdPrefix not null uniqueId ''starts_with'' uniqueIdPrefix, and return.
=== has_notificationTrigger ===
<syntaxhighlight lang="JavaScript">
{
"has_notificationTrigger": {
properties: {
"originTimestamp": {
    type: "number",           // "string" | "number" ...
requiredOnCreate: true,  // default=false
canUpdate: true,          // default = true
validation: {}            // ajv syntax
}
},
storageResources: {
myGraph: {
storageType: "graph",
graphServerTag: "graphHandler"
}
},
links: [
{
storageResourceTags: ["myGraph"],
from: {
objType: {
serviceTag: "NotificationManager",
objectType: "notification"
},
linkType: "one",
handler: true
},
to: {
objType: {
serviceTag: "NotificationManager",
objectType: "notificationTrigger"
},
requiredOnCreate: true,
linkType: "many",
handler: true
}
}
]
}
}
</syntaxhighlight>


= Functions =
== sendNotificationToReceiver ==


=== disabled_notificationTrigger ===
<syntaxhighlight lang="JavaScript">
<syntaxhighlight lang="JavaScript">
/**
{
* Send message to receiver endpoint
"disabled_notificationTrigger": {
* @param {Object} notification - object from the Notifications table
properties: {
* @param {Object} notificationGroup - object from the NotificationGroups table
"originTimestamp": {
* @param {string} notificationBody - string sent to receiver
    type: "number",            // "string" | "number" ...
*/
requiredOnCreate: true,  // default=false
canUpdate: true,          // default = true
validation: {}           // ajv syntax
}
},
storageResources: {
myGraph: {
storageType: "graph",
graphServerTag: "graphHandler"
}
},
links: [
{
storageResourceTags: ["myGraph"],
from: {
objType: {
serviceTag: "NotificationManager",
objectType: "notification"
},
linkType: "one",
handler: true
},
to: {
objType: {
serviceTag: "NotificationManager",
objectType: "notificationTrigger"
},
linkType: "many",
handler: true
}
}
]
}
}
</syntaxhighlight>
</syntaxhighlight>


=== logic ===
= value or values array =
 
* Either 'value' or 'values' should be set, not both, if 'values' is used it means any of the elements in the 'values' array can match.
 
* To achieve this on Activity Switchboard a new trigger group is created for each element, each activity group has the full list of other triggers, and one option from this trigger's ''values'' array. If a notification has multiple ''values'' type triggers than a new trigger group on Activity Switchboard is created for each combination.


# get notificationGroup node by getNodeAndRelationships
* "serviceName" and "topicName" valueTypes do not have propertyName
#* nodeLabel: "notificationGroup"
#* nodeProperties.notificationGroupId
#* relationshipType: "has_notificationGroup"
#* relationshipDirection: "incoming"
#:return; notificationGroup and email node (in future; will have another service eg. SMS, site notification)
# send message to OutNewNotification topic, adding below properties:
#* uniqueId: {emailId}
#* notificationBody
#* additionalData
#* (? filter policy limit) receiverTag gets added to message attributes so can be filtered for by receiving service


;  [[Service - User Contact Manager]] and [[Service - Contact Method Email]] will change logic or structure
= serviceName and topicName triggers =


== disableNotificationGroup ==
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes
* Can set serviceName+topicName, then only messages in that topic will pass
* serviceName and topicName can both be submitted as "values" array, creating multiple trigger groups for each combination


<syntaxhighlight lang="JavaScript">
= Consolidated notifications =
/**
* @param {Object} notificationGroup
*
*/
</syntaxhighlight>


=== logic ===
== How to trigger processing for time based consolidations ==  


# for each notificationId
* ....
## change relationshipType to "disabled_notification" by send message to [[Service - Graph Handler]] InChangeRelationshipType
* maybe can use some sort of queue to store a list of NotificationGroup’s that are due to be sent (either because consolidatedSendIfEmpty == true or there are some activities waiting to be processed)
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup
##* notificationId
## send message to OutNotificationDisabled topic
##* notificationId
# change relationshipType to "disabled_notificationGroup" by send message to [[Service - Graph Handler]] InChangeRelationshipType
# send message to OutNotificationGroupDisabled topic
#* notificationGroupId


== enableNotificationGroup ==
= Formating the notification body =  


<syntaxhighlight lang="JavaScript">
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body
/**
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs
* @param {Object} notificationGroup
*
*/
</syntaxhighlight>


=== logic ===
= Ideas =  


# for each notificationId
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level
## get notification node by GraphHandler getNodeAndRelationships
* If set to ''overview'' maybe can store just aggregate/s needed for the consolidated notification, at the moment store entire messages, create the overview once when sending notification
##* nodeLabel: "notification"
##* nodeProperties.notificationId
##* relationshipType: "has_notificationTrigger"
##* relationshipDirection: "outgoing"
## change relationshipType to "has_notification" by send message to [[Service - Graph Handler]] InChangeRelationshipType
## invoke sharedCreateTriggerGroup.sharedCreateTriggerGroup
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup
###* uniqueId: notificationId
###* additionalData: {}
###* requestedTriggers: [{notificationTriggers}]
## send message to OutNotificationEnabled topic
##* notificationId
# change relationshipType to "has_notificationGroup" by send message to [[Service - Graph Handler]] InChangeRelationshipType
# send message to OutNotificationGroupEnabled topic
#* notificationGroupId


= Sample notification object sent to receiver service =  
= Working documents =


<syntaxhighlight lang="JavaScript">
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]
{
notificationBody: [
{
messageAttributes: {
// xxx: yyy
},
message: {
// full body of message received
}
},
],
additionalData: {
userId: ...
},
uniqueId: {emailId}
}
</syntaxhighlight>


[[Category:Working documents| 2020-11-08]]
[[Category:Backend services| Notification Manager]]
[[Category:Working documents - Notification Manager| 2020-11-08]]

Latest revision as of 02:18, 8 August 2024

Overview

Consolidates and sends notifications to any number of receiver services. Will be used for user notifications but can have other types of receiver services added. Activities are received from Activity Switchboard service.

Notification groups set whether activities are considated (eg per day/per month/per x number of activities), if considated activities are stored here until sending is triggered, according to the considation rules. If not consolidated they are sent on immediately.

Repository

https://bitbucket.org/stb_working/notification-manager/src/master/

DynamoDB tables

PendingConsolidation Table

notificationGroupId (partition key)
type: string
comes from: random UUID
time (sort key)
type: number
{timestamp activity handled in Activity Switchboard}_{small random UUID}
activityMsg
type: object
object containing messageAttributes and message

Object Schemas

Additional Information
Per Service Schemas

objType

notificationGroup

  • Groups many notificatons together
  • Simplifies management of similar notifications
  • Allows for consolidation of notifications at notification group level
{
	objectType: "notificationGroup",
	canDelete: false,
    complexFilterServiceTag: "complexFilter",
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']
		get: [], 				// default: ['hdrApi', 'hdrInv']
		delete: [], 			// default: ['hdrApi', 'hdrSqs']
	},
	addOnDataStructure: [
		{
			type: "versionedData",
			versionedDataLabel: "notificationGroupSettings",
			storageResourceTag: "myGraph",
			fieldNames: [
				{ // fieldName in versionedData should now same in main objectSchema
			 		fieldName: "notificationGroupName",
			 		type: "string",
					optionalOnCreate: true, 				// default = false
					canUpdate: true, 						// default = true
					validation: {
						pattern: pattern
					}
				},
				{ 
					fieldName: "consolidated",
			 		type: "boolean",
					optionalOnCreate: true, 								
					canUpdate: true, 						
					validation: {
						pattern: pattern
					}
				},
				{
					fieldName: "consolidatedType",
			 		type: "string",
					optionalOnCreate: true, 								
					canUpdate: true, 						
					validation: {
						pattern: pattern
					}
				},
				{
					fieldName: "consolidatedFrequency",
			 		type: "string",
					optionalOnCreate: true, 								
					canUpdate: true, 						
					validation: {
						pattern: pattern
					}
				},
				{
					fieldName: "consolidatedNextSendDue",
			 		type: "number",
					optionalOnCreate: true, 								
					canUpdate: true, 						
					validation: {
						pattern: pattern
					}
				},
				{
					fieldName: "consolidatedSendIfEmpty",
			 		type: "boolean",
					optionalOnCreate: true, 				 				
					canUpdate: true, 						
					validation: {
						pattern: pattern
					}
				}
			]
		}
    ],
    // overwriteGeneratedMainFunction: ["update"],
    storageResources: {
		myGraph: {
			storageType: "graph",
			graphServerTag: "GraphHandler"
		}
    },
    fieldNames: { // see Per Service Schemas
		notificationGroupId: {
		    type: "string",
			optionalOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']
		},
		receiverTag: {
			type: "string",
			requiredOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']
		},
		userId: {
			type: "string",
			requiredOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']
		}
    },
    identifiers: [
		{
			type: "identifier",
			fieldName: "notificationGroupId"
		}
    ]
}

fieldNames

notificationGroupId (identifier)
comes from: random UUID
notificationGroupName
string name set by reciever service, optional
receiverTag
set by the creating service name
eg ContactMethodEmail
consolidated
true|false
consolidatedType
overview|detailed
overview sends a count of each type of notification, detailed lists each activity message
consolidatedFrequency
not sure, maybe use some sort of standard like cron
consolidatedNextSendDue
timestamp for next due time to send notification
consolidatedSendIfEmpty
true|false
userId
for createdBy relationship


notification

{
	objectType: "notification",
	canDelete: false,
	complexFilterServiceTag: "complexFilter",
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']
		get: [], 				// default: ['hdrApi', 'hdrInv']
		delete: [], 			// default: ['hdrApi', 'hdrSqs']
	},
	addOnDataStructure: [
		{
			type: "versionedData",
			versionedDataLabel: "notificationSettings",
			storageResourceTag: "myGraph",
			fieldNames: [
				{ // fieldName in versionedData should now same in main objectSchema
			 		fieldName: "notificationName",
			 		type: "string",
					optionalOnCreate: true, 				// default = false
					canUpdate: true, 						// default = true
					validation: {
						pattern: pattern
					}
				}
			]
		}
    ],
   // overwriteGeneratedMainFunction: ["update"],
    storageResources: {
		myGraph: {
			storageType: "graph",
			graphServerTag: "GraphHandler"
		}
    },
    fieldNames: {
		notificationId: {
		    type: "string",
			optionalOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']
		},
		userId: {
			type: "string",
			requiredOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']
		}
    },
    identifiers: [
		{
			type: "identifier",
			fieldName: "notificationId"
		}
    ]
}

fieldNames

notificationId (identifier)
comes from: random UUID
notificationName
string name set by reciever service, optional
userId
for createdBy relationship


notificationTrigger

{
	objectType: "notificationTrigger",
	canDelete: false,
    complexFilterServiceTag: "complexFilter",
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']
		update: [], 			// default: ['hdrApi', 'hdrSqs']
		get: [], 				// default: ['hdrApi', 'hdrInv']
		delete: [], 			// default: ['hdrApi', 'hdrSqs']
	},
    // overwriteGeneratedMainFunction: ["create"],
    storageResources: {
		myGraph: {
			storageType: "graph",
			graphServerTag: "GraphHandler"
		}
    },
    fieldNames: {
		notificationTriggerId: {
			type: "string",
			optionalOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']
		},
		propertyName: {
			type: "string",
			optionalOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']
		},
		valueType: {
			type: "string",
			requiredOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']
		},
		valueString: {
			type: "string",
			optionalOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']		
		},
		valueNumber: {
			type: "number",
			optionalOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']		
		},
		values: {
			type: "arrayMixed",
			optionalOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']		
		},
		userId: {
			type: "string",
			requiredOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['myGraph']
		}
    },
    identifiers: [
		{
			type: "identifier",
			fieldName: "notificationTriggerId"
		}
    ]
}

fieldNames

notificationTriggerId (identifier)
comes from: random UUID
propertyName
can be a nested property, use dot notation
"serviceName" and "topicName" valueTypes do not have propertyName
valueType
property|attribute|serviceName|topicName
valueString
type: string
valueNumber
type: number
values
type: arrayMixed
userId
for createdBy relationship


consolidated

{
	objectType: "consolidated",
	canDelete: false,
    complexFilterServiceTag: "complexFilter",
    storageResources: {
		dynamoDB: {
			storageType: "dynamoDB",
			tableName: "PendingConsolidation"
		}
    },
    fieldNames: {
		notificationGroupId: {
			type: "string",
			requiredOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['dynamoDB'],
			fromServiceNameTag: "NotificationMgr" ,
			fromObjectType: "notificationGroup"
		},
		time: {
			type: "string",
			requiredOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['dynamoDB']		
		},
		activityMsg: {
			type: "object",
			requiredOnCreate: true,
			canUpdate: false,
			validation: {
				pattern: pattern
			},
			storageResourceTags: ['dynamoDB']		
		}
    },
    identifiers: [
		{
			type: "partitionKey",
			fieldName: "notificationGroupId"
		},
		{
			type: "sortKey",
			fieldName: "time"
		}
    ]
}

fieldNames

notificationGroupId (partition key)
comes from: random UUID
time (sort key)
comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}
adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId
activityMsg
copy of the message delivered from Activity Switchboard


Object Relationships

has_notificationGroup

{
	"has_notificationGroup": {
		properties: {
			"originTimestamp": {
			    type: "number",            	// "string" | "number" ...
				requiredOnCreate: true,  	// default=false
				canUpdate: true,          	// default = true
				validation: {}            	// ajv syntax
			}
		},
		storageResources: {
			myGraph: {
				storageType: "graph",
				graphServerTag: "graphHandler"			
			}
		},
		links: [
			{
				storageResourceTags: ["myGraph"],
				from: {
					objType: {
						serviceTag: "UserContactManager",					
						objectType: "userContact"
					},
					linkType: "one",
				},
				to: {
					objType: {
						serviceTag: "NotificationManager",				
						objectType: "notificationGroup"					
					},
					requiredOnCreate: true,					
					linkType: "many",
					handler: true
				}
			}
		]
	}
}
links userContact to notificationGroup

disabled_notificationGroup

{
	"disabled_notificationGroup": {
		properties: {
			"originTimestamp": {
			    type: "number",            	// "string" | "number" ...
				requiredOnCreate: true,  	// default=false
				canUpdate: true,          	// default = true
				validation: {}            	// ajv syntax
			}
		},
		storageResources: {
			myGraph: {
				storageType: "graph",
				graphServerTag: "GraphHandler"			
			}
		},
		links: [
			{
				storageResourceTags: ["myGraph"],
				from: {
					objType: {
						serviceTag: "UserContactManager",					
						objectType: "userContact"
					},
					linkType: "one"
				},
				to: {
					objType: {
						serviceTag: "NotificationManager",				
						objectType: "notificationGroup"					
					},			
					linkType: "many",
					handler: true
				}
			}
		]
	}
}


has_notification

{
	"has_notification": {
		properties: {
			"originTimestamp": {
			    type: "number",            	// "string" | "number" ...
				requiredOnCreate: true,  	// default=false
				canUpdate: true,          	// default = true
				validation: {}            	// ajv syntax
			}
		},
		storageResources: {
			myGraph: {
				storageType: "graph",
				graphServerTag: "GraphHandler"			
			}
		},
		links: [
			{
				storageResourceTags: ["myGraph"],
				from: {
					objType: {
						serviceTag: "NotificationManager",					
						objectType: "notificationGroup"
					},
					linkType: "one",
					handler: true					
				},
				to: {
					objType: {
						serviceTag: "NotificationManager",				
						objectType: "notification"					
					},
					requiredOnCreate: true,					
					linkType: "many",
					handler: true
				}
			}
		]
	}
}


disabled_notification

{
	"disabled_notification": {
		properties: {
			"originTimestamp": {
			    type: "number",            	// "string" | "number" ...
				requiredOnCreate: true,  	// default=false
				canUpdate: true,          	// default = true
				validation: {}            	// ajv syntax
			}
		},
		storageResources: {
			myGraph: {
				storageType: "graph",
				graphServerTag: "graphHandler"			
			}
		},
		links: [
			{
				storageResourceTags: ["myGraph"],
				from: {
					objType: {
						serviceTag: "NotificationManager",					
						objectType: "notificationGroup"
					},
					linkType: "one",
					handler: true
				},
				to: {
					objType: {
						serviceTag: "NotificationManager",				
						objectType: "notification"					
					},
					linkType: "many",
					handler: true
				}
			}
		]
	}
}


has_notificationTrigger

{
	"has_notificationTrigger": {
		properties: {
			"originTimestamp": {
			    type: "number",            	// "string" | "number" ...
				requiredOnCreate: true,  	// default=false
				canUpdate: true,          	// default = true
				validation: {}            	// ajv syntax
			}
		},
		storageResources: {
			myGraph: {
				storageType: "graph",
				graphServerTag: "graphHandler"			
			}
		},
		links: [
			{
				storageResourceTags: ["myGraph"],
				from: {
					objType: {
						serviceTag: "NotificationManager",					
						objectType: "notification"
					},
					linkType: "one",
					handler: true
				},
				to: {
					objType: {
						serviceTag: "NotificationManager",				
						objectType: "notificationTrigger"					
					},
					requiredOnCreate: true,	
					linkType: "many",
					handler: true
				}
			}
		]
	}
}


disabled_notificationTrigger

{
	"disabled_notificationTrigger": {
		properties: {
			"originTimestamp": {
			    type: "number",            	// "string" | "number" ...
				requiredOnCreate: true,  	// default=false
				canUpdate: true,          	// default = true
				validation: {}            	// ajv syntax
			}
		},
		storageResources: {
			myGraph: {
				storageType: "graph",
				graphServerTag: "graphHandler"			
			}
		},
		links: [
			{
				storageResourceTags: ["myGraph"],
				from: {
					objType: {
						serviceTag: "NotificationManager",					
						objectType: "notification"
					},
					linkType: "one",
					handler: true
				},
				to: {
					objType: {
						serviceTag: "NotificationManager",				
						objectType: "notificationTrigger"					
					},
					linkType: "many",
					handler: true
				}
			}
		]
	}
}

value or values array

  • Either 'value' or 'values' should be set, not both, if 'values' is used it means any of the elements in the 'values' array can match.
  • To achieve this on Activity Switchboard a new trigger group is created for each element, each activity group has the full list of other triggers, and one option from this trigger's values array. If a notification has multiple values type triggers than a new trigger group on Activity Switchboard is created for each combination.
  • "serviceName" and "topicName" valueTypes do not have propertyName

serviceName and topicName triggers

  • Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass
  • Can set only the serviceName, then any message that passess the properties and is from that serviceName passes
  • Can set serviceName+topicName, then only messages in that topic will pass
  • serviceName and topicName can both be submitted as "values" array, creating multiple trigger groups for each combination

Consolidated notifications

How to trigger processing for time based consolidations

  • ....
  • maybe can use some sort of queue to store a list of NotificationGroup’s that are due to be sent (either because consolidatedSendIfEmpty == true or there are some activities waiting to be processed)

Formating the notification body

  • Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body
  • The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs

Ideas

  • overview notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level
  • If set to overview maybe can store just aggregate/s needed for the consolidated notification, at the moment store entire messages, create the overview once when sending notification

Working documents

Working documents - Notification Manager