<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://izara.io/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Pong</id>
	<title>Izara Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://izara.io/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Pong"/>
	<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php/Special:Contributions/Pong"/>
	<updated>2026-05-03T10:43:31Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.38.4</generator>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2026-03-02_-_System_Notifications&amp;diff=4241</id>
		<title>2026-03-02 - System Notifications</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2026-03-02_-_System_Notifications&amp;diff=4241"/>
		<updated>2026-03-23T00:07:46Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Notes = &lt;br /&gt;
&lt;br /&gt;
* System notifications can be sent by any flow, eg Issues juror request &lt;br /&gt;
* Want to central list available for example notification page&lt;br /&gt;
* Each project has it's own system notifications, manages it's own notifcation schema&lt;br /&gt;
&lt;br /&gt;
= Data Structures =&lt;br /&gt;
&lt;br /&gt;
* notificationType and notificationTypeId standard data structures&lt;br /&gt;
* userSystemNotification objType in UserContactManager in dynamo&lt;br /&gt;
* defaultNotificationUserContact in graph&lt;br /&gt;
&lt;br /&gt;
= Notification Schema =&lt;br /&gt;
&lt;br /&gt;
* Each service has a file in schema folder for notifications the service is responsible for&lt;br /&gt;
* Grouping categorizes notification, for example to show on notification setting page&lt;br /&gt;
* Grouping categories can be shared across multiple services&lt;br /&gt;
* Weight affects the notification's order when listed with other notifications, eg: per category in notification settings&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
	{&lt;br /&gt;
		notificationTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
		notificationGrouping: &amp;quot;yyy&amp;quot;,&lt;br /&gt;
		weight: &amp;quot;##&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	// ...&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Generating S3 Schema =&lt;br /&gt;
&lt;br /&gt;
* After deploy service can invoke to update S3 notifcation schemas for that service&lt;br /&gt;
* Iterate service's notificaton schema and add notificationTag accroding to it's notificationGrouping&lt;br /&gt;
* Add serviceTag so each notification is identified by it's serviceTag and notificationTag&lt;br /&gt;
&lt;br /&gt;
= Code Usage =&lt;br /&gt;
&lt;br /&gt;
* In code specify the notificationTag and notification content, and userId (can set serviceTag to default to current service)&lt;br /&gt;
* Send to userContactManager sendSystemNotification flow&lt;br /&gt;
&lt;br /&gt;
= Example data sent to ContactManager =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
      {&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot;,&lt;br /&gt;
	      value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
	      values: [ // array of messages&lt;br /&gt;
		     {&lt;br /&gt;
		          properties: [&lt;br /&gt;
			         {&lt;br /&gt;
				       label: &amp;quot;qqq&amp;quot;,&lt;br /&gt;
				       value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
			         },&lt;br /&gt;
			         {&lt;br /&gt;
				       label: &amp;quot;rrr&amp;quot;,&lt;br /&gt;
				       value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
			         },&lt;br /&gt;
			         // ..&lt;br /&gt;
                  ]&lt;br /&gt;
		     },&lt;br /&gt;
		     // ...&lt;br /&gt;
	      ]&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot;, // user defined label for result&lt;br /&gt;
      },&lt;br /&gt;
      // ...&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2026-03-02]]&lt;br /&gt;
[[Category:Working_documents - User Contact Manager| 2026-03-02]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2026-03-02_-_System_Notifications&amp;diff=4240</id>
		<title>2026-03-02 - System Notifications</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2026-03-02_-_System_Notifications&amp;diff=4240"/>
		<updated>2026-03-23T00:06:25Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Notes = &lt;br /&gt;
&lt;br /&gt;
* System notifications can be sent by any flow, eg Issues juror request &lt;br /&gt;
* Want to central list available for example notification page&lt;br /&gt;
* Each project has it's own system notifications, manages it's own notifcation schema&lt;br /&gt;
&lt;br /&gt;
= Data Structures =&lt;br /&gt;
&lt;br /&gt;
* notificationType and notificationTypeId standard data structures&lt;br /&gt;
* userSystemNotification objType in UserContactManager in dynamo&lt;br /&gt;
* defaultNotificationUserContact in graph&lt;br /&gt;
&lt;br /&gt;
= Notification Schema =&lt;br /&gt;
&lt;br /&gt;
* Each service has a file in schema folder for notifications the service is responsible for&lt;br /&gt;
* Grouping categorizes notification, for example to show on notification setting page&lt;br /&gt;
* Grouping categories can be shared across multiple services&lt;br /&gt;
* Weight affects the notification's order when listed with other notifications, eg: per category in notification settings&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
	{&lt;br /&gt;
		notificationTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
		notificationGrouping: &amp;quot;yyy&amp;quot;,&lt;br /&gt;
		weight: &amp;quot;##&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	// ...&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Generating S3 Schema =&lt;br /&gt;
&lt;br /&gt;
* After deploy service can invoke to update S3 notifcation schemas for that service&lt;br /&gt;
* Iterate service's notificaton schema and add notificationTag accroding to it's notificationGrouping&lt;br /&gt;
* Add serviceTag so each notification is identified by it's serviceTag and notificationTag&lt;br /&gt;
&lt;br /&gt;
= Code Usage =&lt;br /&gt;
&lt;br /&gt;
* In code specify the notificationTag and notification content, and userId (can set serviceTag to default to current service)&lt;br /&gt;
* Send to userContactManager sendSystemNotification flow&lt;br /&gt;
&lt;br /&gt;
== example data sent to ContactManager ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
      {&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot;,&lt;br /&gt;
	      value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
	      values: [ // array of messages&lt;br /&gt;
		     {&lt;br /&gt;
		          properties: [&lt;br /&gt;
			         {&lt;br /&gt;
				       label: &amp;quot;qqq&amp;quot;,&lt;br /&gt;
				       value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
			         },&lt;br /&gt;
			         {&lt;br /&gt;
				       label: &amp;quot;rrr&amp;quot;,&lt;br /&gt;
				       value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
			         },&lt;br /&gt;
			         // ..&lt;br /&gt;
                  ]&lt;br /&gt;
		     },&lt;br /&gt;
		     // ...&lt;br /&gt;
	      ]&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot;, // user defined label for result&lt;br /&gt;
      },&lt;br /&gt;
      // ...&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2026-03-02]]&lt;br /&gt;
[[Category:Working_documents - User Contact Manager| 2026-03-02]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Standard_Data_Structures&amp;diff=4160</id>
		<title>Standard Data Structures</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Standard_Data_Structures&amp;diff=4160"/>
		<updated>2026-03-10T05:10:57Z</updated>

		<summary type="html">&lt;p&gt;Pong: /* notificationTypeId */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
Standard structure and definitions.&lt;br /&gt;
&lt;br /&gt;
= Definition =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	serviceTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	objectType: &amp;quot;yyy&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
use to identify which serviceTag that object schema located&lt;br /&gt;
* serviceTag: service tag that stored object schema&lt;br /&gt;
* objectType: name of object schema&lt;br /&gt;
&lt;br /&gt;
== objTypeConcat ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;serviceTag&amp;gt;_&amp;lt;objectType&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* objTypeConcat: &amp;quot;serviceTag&amp;quot; + &amp;quot;_&amp;quot; + &amp;quot;objectType&amp;quot;&lt;br /&gt;
* now use as the name of node label in graph database&lt;br /&gt;
&lt;br /&gt;
== objTypeId ==&lt;br /&gt;
* hash of objType object&lt;br /&gt;
&lt;br /&gt;
== relType ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	serviceTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	relationshipTag: &amp;quot;yyy&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
use to identify which serviceTag that relationship schema  located&lt;br /&gt;
* serviceTag: service tag that stored object schema&lt;br /&gt;
* relationshipTag: name of relationship schema&lt;br /&gt;
&lt;br /&gt;
== relTypeConcat ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;serviceTag&amp;gt;_&amp;lt;relationshipTag&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* relTypeConcat: &amp;quot;serviceTag&amp;quot; + &amp;quot;_&amp;quot; + &amp;quot;relationshipTag&amp;quot;&lt;br /&gt;
* now use as the name of relationshipType in graph database&lt;br /&gt;
&lt;br /&gt;
== relTypeId ==&lt;br /&gt;
* hash of relType object&lt;br /&gt;
&lt;br /&gt;
== relTypeAndDirection ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	relType: {&lt;br /&gt;
		serviceTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
		relationshipTag: &amp;quot;yyy&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	relationshipDirection: &amp;quot;to&amp;quot;, // &amp;quot;from&amp;quot; || &amp;quot;to&amp;quot;&lt;br /&gt;
    relationshipProperties: {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== objectLinks ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
  {&lt;br /&gt;
    base:{&lt;br /&gt;
      objType:{&lt;br /&gt;
        serviceTag: &amp;quot;xx&amp;quot;,&lt;br /&gt;
        objectType: &amp;quot;yy&amp;quot;,&lt;br /&gt;
      },&lt;br /&gt;
      requiredOnCreate:true,&lt;br /&gt;
      linkType:&amp;quot;one&amp;quot;,&lt;br /&gt;
      direction:&amp;quot;to&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    other:{&lt;br /&gt;
      objType:{&lt;br /&gt;
        serviceTag: &amp;quot;xx&amp;quot;,&lt;br /&gt;
        objectType: &amp;quot;yy&amp;quot;,&lt;br /&gt;
      },&lt;br /&gt;
      requiredOnCreate:false,&lt;br /&gt;
      linkType:&amp;quot;one&amp;quot;,&lt;br /&gt;
      direction:&amp;quot;from&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    relType:{&lt;br /&gt;
      serviceTag:&amp;quot;xx&amp;quot;&lt;br /&gt;
      relationshipTag:&amp;quot;xx&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  // ... another objectLinks&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* This format will receive it when use function getObjectLinks and base objType use it own objType.&lt;br /&gt;
* It show all link that connect with that objType.&lt;br /&gt;
&lt;br /&gt;
== objectLink ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  base:{&lt;br /&gt;
    objType:{&lt;br /&gt;
      serviceTag: &amp;quot;xx&amp;quot;,&lt;br /&gt;
      objectType: &amp;quot;yy&amp;quot;,&lt;br /&gt;
    },&lt;br /&gt;
    requiredOnCreate:true,&lt;br /&gt;
    linkType:&amp;quot;one&amp;quot;,&lt;br /&gt;
    direction:&amp;quot;to&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  other:{&lt;br /&gt;
    objType:{&lt;br /&gt;
      serviceTag: &amp;quot;xx&amp;quot;,&lt;br /&gt;
      objectType: &amp;quot;yy&amp;quot;,&lt;br /&gt;
    },&lt;br /&gt;
    requiredOnCreate:false,&lt;br /&gt;
    linkType:&amp;quot;one&amp;quot;,&lt;br /&gt;
    direction:&amp;quot;from&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  relType:{&lt;br /&gt;
    serviceTag:&amp;quot;xx&amp;quot;&lt;br /&gt;
    relationshipTag:&amp;quot;xx&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Object structure of one objectLink&lt;br /&gt;
&lt;br /&gt;
== linkConfig ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  from: {&lt;br /&gt;
    serviceTag: &amp;quot;xx&amp;quot;,&lt;br /&gt;
    objectType: &amp;quot;yy&amp;quot;,&lt;br /&gt;
  },&lt;br /&gt;
  to: {&lt;br /&gt;
    serviceTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
    objectType: &amp;quot;yyy&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  relType: {&lt;br /&gt;
    serviceTag: &amp;quot;xxxx&amp;quot;,&lt;br /&gt;
    relationshipTag: &amp;quot;yyyy&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  relationshipDirection: &amp;quot;to&amp;quot;, // &amp;quot;from&amp;quot; || &amp;quot;to&amp;quot;&lt;br /&gt;
  requestProperties: {&lt;br /&gt;
    // to calculate orderPrices we need one identifier for each of the below objTypes&lt;br /&gt;
    tag_deliverTo: {&lt;br /&gt;
      requestPropertyType: &amp;quot;objectIdentifier&amp;quot;,&lt;br /&gt;
      objType: {&lt;br /&gt;
        serviceTag: &amp;quot;Locations&amp;quot;,&lt;br /&gt;
        objectType: &amp;quot;locationNode&amp;quot; // deliversToLocationNode&lt;br /&gt;
      },&lt;br /&gt;
      identifiersFieldName: &amp;quot;locationNodeId&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    // ... other request properties&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
config of each link between 2 objType&lt;br /&gt;
&lt;br /&gt;
== linkType ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  firstObjType: {&lt;br /&gt;
    serviceTag: &amp;quot;xx&amp;quot;,&lt;br /&gt;
    objectType: &amp;quot;yy&amp;quot;,&lt;br /&gt;
  },&lt;br /&gt;
  secondObjType: {&lt;br /&gt;
    serviceTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
    objectType: &amp;quot;yyy&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  relType: {&lt;br /&gt;
    serviceTag: &amp;quot;xxxx&amp;quot;,&lt;br /&gt;
    relationshipTag: &amp;quot;yyyy&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  relationshipDirection: &amp;quot;to&amp;quot; // &amp;quot;from&amp;quot; || &amp;quot;to&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
use to identify relationship between 2 objectSchema&lt;br /&gt;
&lt;br /&gt;
== linkTypeId ==&lt;br /&gt;
create hash id from linkType&lt;br /&gt;
&lt;br /&gt;
== pathLinkType ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  objType: {&lt;br /&gt;
    serviceTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
    objectType: &amp;quot;yyy&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  relType: {&lt;br /&gt;
    serviceTag: &amp;quot;xxxx&amp;quot;,&lt;br /&gt;
    relationshipTag: &amp;quot;yyyy&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  relationshipDirection: &amp;quot;to&amp;quot; // &amp;quot;from&amp;quot; || &amp;quot;to&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* pathLinkType is used at each step of traversal/path&lt;br /&gt;
* when joined with the previous objType/baseObjType can be used to create linkType (and request linkConfig)&lt;br /&gt;
&lt;br /&gt;
== pathLinkTypeId ==&lt;br /&gt;
* create hash id from pathLinkType&lt;br /&gt;
&lt;br /&gt;
== flowType ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	serviceTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	flowTag: &amp;quot;yyy&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
use to identify which serviceTag that flow schema is located&lt;br /&gt;
&lt;br /&gt;
== systemTextType ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	namespace: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	systemTextTag: &amp;quot;yyy&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
identify a system text&lt;br /&gt;
&lt;br /&gt;
== notificationType ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	serviceTag: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	notificationTag: &amp;quot;yyy&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
identify a system notification&lt;br /&gt;
&lt;br /&gt;
== notificationTypeId ==&lt;br /&gt;
&lt;br /&gt;
* hash of notificationType object&lt;br /&gt;
&lt;br /&gt;
== identifiers ==&lt;br /&gt;
An object that has fieldName’s identifiers inside&lt;br /&gt;
=== example identifiers ===&lt;br /&gt;
* example identifiers inside objectSchema&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
identifiers:[&lt;br /&gt;
  {    &lt;br /&gt;
    type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
    fieldNames: [&amp;quot;xx&amp;quot;,&amp;quot;yy&amp;quot;],&lt;br /&gt;
    name: &amp;quot;zz&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* example identifiers object&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{    &lt;br /&gt;
  xx:&amp;quot;xx value&amp;quot;,&lt;br /&gt;
  yy:&amp;quot;yy value&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The example above identifiers is the value of fieldNames that part of identifiers &lt;br /&gt;
&lt;br /&gt;
== identifiersBase ==&lt;br /&gt;
concatenated identifiers object &lt;br /&gt;
=== example identifiersBase ===&lt;br /&gt;
* example identifiers inside objectSchema&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
identifiers:[&lt;br /&gt;
  {    &lt;br /&gt;
    type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
    fieldNames: [&amp;quot;xx&amp;quot;,&amp;quot;yy&amp;quot;],&lt;br /&gt;
    name: &amp;quot;zz&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* example identifiersBase object&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{    &lt;br /&gt;
  zz:&amp;quot;xxValue_yyValue&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The example above identifiersBase is the value of each identifier of objectSchema &lt;br /&gt;
&lt;br /&gt;
== identifiersId ==&lt;br /&gt;
hash of identifiers object &lt;br /&gt;
&lt;br /&gt;
== identifiersConcat ==&lt;br /&gt;
string that concatenate all identifiers&lt;br /&gt;
* Note!: Will ordered concatenate value by objectSchema.identifiers array &lt;br /&gt;
&lt;br /&gt;
== objInstanceBase ==&lt;br /&gt;
result from dynamoDB that contains relevant and not relevant data of objectSchema &lt;br /&gt;
* identifiers from in objInstanceBase will concatenated or not concatenated depending on objectSchema&lt;br /&gt;
* izContext field not relevant with objectSchema but it contains inside objInstanceBase&lt;br /&gt;
&lt;br /&gt;
== objInstance ==&lt;br /&gt;
similar objInstanceBase but split concatenateIdentifier to each fieldName&lt;br /&gt;
&lt;br /&gt;
== objInstanceFull ==&lt;br /&gt;
object that separated fields and identifiers and identifiers cannot concatenated&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{    &lt;br /&gt;
  identifiers:{&lt;br /&gt;
    //... identifiers fieldName of objectSchema&lt;br /&gt;
  }, &lt;br /&gt;
  fields:{&lt;br /&gt;
    //.. data of objectSchema&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* identifiers: contains identifiers data&lt;br /&gt;
* fields: another data except  &lt;br /&gt;
&lt;br /&gt;
== example data  ==&lt;br /&gt;
=== example objectSchema ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// example objectSchema&lt;br /&gt;
{    &lt;br /&gt;
  fieldNames:{&lt;br /&gt;
    id:{&lt;br /&gt;
      type:”string”,&lt;br /&gt;
      // … another fieldName settings&lt;br /&gt;
    },&lt;br /&gt;
    createTime: {},     //  filedName settings&lt;br /&gt;
    location: {},       //  filedName settings&lt;br /&gt;
    value: {},          //  filedName settings&lt;br /&gt;
  },&lt;br /&gt;
  identifiers:[&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
      fieldNames: [&amp;quot;id&amp;quot;,&amp;quot;createTime&amp;quot;],   // composite partition keys in DynamoDB&lt;br /&gt;
      deliminator: &amp;quot;_&amp;quot;,                  // optional, defaults to &amp;quot;_&amp;quot;&lt;br /&gt;
      name: &amp;quot;createId&amp;quot;                   // name of field in database&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
      fieldName: “location”&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== objInstanceBase ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// example objInstanceBase data&lt;br /&gt;
{&lt;br /&gt;
  createId:&amp;quot;id_createTime&amp;quot;,&lt;br /&gt;
  location:&amp;quot;cnx&amp;quot;,&lt;br /&gt;
  value:199,&lt;br /&gt;
  errorsFound:[],&lt;br /&gt;
  izContext:{&lt;br /&gt;
  // ...izContext&lt;br /&gt;
  },&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Normally objInstanceBase is the result from dynamoDB and it will include data that is not relevant to objectSchema. In example above errorsFound and izContext not contain in objectSchema.&lt;br /&gt;
&lt;br /&gt;
=== objInstance ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// example objInstance data&lt;br /&gt;
{&lt;br /&gt;
  id:&amp;quot;id&amp;quot;,&lt;br /&gt;
  createTime:&amp;quot;createTime&amp;quot;&lt;br /&gt;
  location:&amp;quot;cnx&amp;quot;,&lt;br /&gt;
  value:199,&lt;br /&gt;
  errorsFound:[],&lt;br /&gt;
  izContext:{&lt;br /&gt;
  // ...izContext&lt;br /&gt;
  },&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
objInstance is object that already refactored identifiers but another fields same as objInstanceBase &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== objInstanceFull ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// example objInstanceFull data&lt;br /&gt;
{    &lt;br /&gt;
  identifiers:{&lt;br /&gt;
    id:&amp;quot;id&amp;quot;,&lt;br /&gt;
    createTime:&amp;quot;createTime&amp;quot;&lt;br /&gt;
    location:&amp;quot;cnx&amp;quot;,&lt;br /&gt;
  }, &lt;br /&gt;
  fields:{&lt;br /&gt;
    value:199,&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== identifiersBase ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// example identifiersBase data&lt;br /&gt;
{&lt;br /&gt;
  createId:&amp;quot;id_createTime&amp;quot;,&lt;br /&gt;
  location:&amp;quot;cnx&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== identifiers ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// example identifiers data&lt;br /&gt;
{&lt;br /&gt;
  id: &amp;quot;id&amp;quot;,&lt;br /&gt;
  createTime: &amp;quot;createTime&amp;quot;,&lt;br /&gt;
  location: &amp;quot;cnx&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Standard Data Structures| Standard Data Structures]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=3553</id>
		<title>Service - User Contact Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=3553"/>
		<updated>2025-09-12T06:47:35Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Manages contact methods available to users. Records each users contacts.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-user-contact-manager&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== userContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  objectType: &amp;quot;userContact&amp;quot;,&lt;br /&gt;
  canDelete: false,&lt;br /&gt;
  addOnDataStructure: [&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
      versionedDataLabel: &amp;quot;userContactSettings&amp;quot;,&lt;br /&gt;
      storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
      fieldNames: {&lt;br /&gt;
	    &amp;quot;contactTag&amp;quot;: {&lt;br /&gt;
	      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
	      optionalOnCreate: true,&lt;br /&gt;
	      canUpdate: true,&lt;br /&gt;
	      validation: {&lt;br /&gt;
	        pattern: pattern&lt;br /&gt;
	      }&lt;br /&gt;
	    }&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ],&lt;br /&gt;
  storageResources: {&lt;br /&gt;
    dynamoDB: {&lt;br /&gt;
      storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
      tableName: &amp;quot;UserContactRecords&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    myGraph: {&lt;br /&gt;
      storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
      graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  fieldNames: {&lt;br /&gt;
    userContactId: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      randomOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['myGraph', 'dynamoDB']&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  identifiers: [&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
      fieldName: &amp;quot;userContactId&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; userContactId&lt;br /&gt;
: comes from random UUID&lt;br /&gt;
; contactTag&lt;br /&gt;
: string name set by user&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== hasUserContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;hasUserContact&amp;quot;: {&lt;br /&gt;
    canChangeToRelTypes: [&lt;br /&gt;
      {&lt;br /&gt;
	    serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
	    relationshipTag: &amp;quot;disabledUserContact&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
    ],&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
      &amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
	    type: &amp;quot;number&amp;quot;,            	&lt;br /&gt;
	    requiredOnCreate: true,  	// default = false&lt;br /&gt;
	    canUpdate: true,          	// default = true&lt;br /&gt;
	    validation: {}            	// ajv syntax&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
      myGraph: {&lt;br /&gt;
	    storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
	    graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    links: [&lt;br /&gt;
      {&lt;br /&gt;
	    storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
	    from: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;UserAccount&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
	    },&lt;br /&gt;
	    to: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      requiredOnCreate: true,&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
	      handler: true&lt;br /&gt;
	    }&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledUserContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;disabledUserContact&amp;quot;: {&lt;br /&gt;
    canChangeToRelTypes: [&lt;br /&gt;
      {&lt;br /&gt;
	    serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
	    relationshipTag: &amp;quot;hasUserContact&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
    ],&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
      &amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
	    type: &amp;quot;number&amp;quot;,          &lt;br /&gt;
	    requiredOnCreate: true,  	// default = false&lt;br /&gt;
	    canUpdate: true,          	// default = true&lt;br /&gt;
	    validation: {}            	// ajv syntax&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
      myGraph: {&lt;br /&gt;
    	storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
	    graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    links: [&lt;br /&gt;
      {&lt;br /&gt;
	    storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
	    from: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;UserAccount&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
    	  },&lt;br /&gt;
	      linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
    	},&lt;br /&gt;
    	to: {&lt;br /&gt;
    	  objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      requiredOnCreate: true,&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
	      handler: true&lt;br /&gt;
	    }&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== hasNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;hasNotificationGroup&amp;quot;: {&lt;br /&gt;
    canChangeToRelTypes: [&lt;br /&gt;
      {&lt;br /&gt;
    	serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
	    relationshipTag: &amp;quot;disabledNotificationGroup&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
    ],&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
      &amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
    	type: &amp;quot;number&amp;quot;,           &lt;br /&gt;
	    requiredOnCreate: true,  	// default = false&lt;br /&gt;
	    canUpdate: true,          	// default = true&lt;br /&gt;
	    validation: {}            	// ajv syntax&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
      myGraph: {&lt;br /&gt;
	    storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
	    graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    links: [&lt;br /&gt;
      {&lt;br /&gt;
    	storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
    	from: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
	    },&lt;br /&gt;
	    to: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;NotificationManager&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
	      handler: true&lt;br /&gt;
    	}&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;disabledNotificationGroup&amp;quot;: {&lt;br /&gt;
    canChangeToRelTypes: [&lt;br /&gt;
      {&lt;br /&gt;
    	serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
    	relationshipTag: &amp;quot;hasNotificationGroup&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
    ],&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
      &amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
	    type: &amp;quot;number&amp;quot;,          &lt;br /&gt;
	    requiredOnCreate: true,  	// default = false&lt;br /&gt;
	    canUpdate: true,          	// default = true&lt;br /&gt;
	    validation: {}            	// ajv syntax&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
      myGraph: {&lt;br /&gt;
    	storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
    	graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    links: [&lt;br /&gt;
      {&lt;br /&gt;
    	storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
	    from: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
	    },&lt;br /&gt;
	    to: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;NotificationManager&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
	      handler: true&lt;br /&gt;
    	}&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Creating a new user contact =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of available contact methods&lt;br /&gt;
* User choses one and and is directed to that contact method handler to create the actual entry&lt;br /&gt;
* Once the user contact is created in the handler service a message gets sent to this service which creates it's own UserContacts record&lt;br /&gt;
&lt;br /&gt;
= Creating a new notification group =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of the users contacts&lt;br /&gt;
* How do we handle authentication, notification manager has no record of the userId eg for notificationGroups, only UCM has this&lt;br /&gt;
* (for now not direct to notification manager, any updates go through UCM, but if can figure out some token system could go direct to notification manager) User choses one and is directed to [[Service - Notification Manager|Notification Manager]] service&lt;br /&gt;
* For the endpoint given to Notification Manager try having notifications go directly to the contact method handler. Another option would be to pass back through this service but I cannot see any benefit in this.&lt;br /&gt;
* Notification Manager completes the flow, creating a notification group and associated notification records&lt;br /&gt;
&lt;br /&gt;
= Location specific contact methods =&lt;br /&gt;
&lt;br /&gt;
* Could add ways of restricting the available contact methods to ones the user is likely to want&lt;br /&gt;
* eg: by location (eg country)&lt;br /&gt;
* but have a way that the user can choose any method&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
* If method handler changes the contactName, a message will be sent to User Contact Manager to update its record, contactName is part of the sort key, not sure if it can be updated, if not need to copy the record to a new record with the updated contactName&lt;br /&gt;
* If method handler disables a contact, disable here and send message to [[Service - Notification Manager|Notification Manager]] service which will disable notifications and tell [[Service - Activity Switchboard|Activity Switchboard]] service to remove all associated triggers&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - User Contact Manager|User Contact Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| User Contact Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=3552</id>
		<title>Service - Notification Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=3552"/>
		<updated>2025-09-11T05:59:52Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-notification-manager&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== notificationGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggerGroups together&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  objectType: &amp;quot;notificationGroup&amp;quot;,&lt;br /&gt;
  canDelete: false,&lt;br /&gt;
  storageResources: {&lt;br /&gt;
    myGraph: {&lt;br /&gt;
      storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
      graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    dynamoDB: {&lt;br /&gt;
      storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
      tableName: &amp;quot;NotificationGroupProcessing&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  addOnDataStructure: [&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
      versionedDataLabel: &amp;quot;notificationGroupSettings&amp;quot;,&lt;br /&gt;
      storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
      fieldNames: {&lt;br /&gt;
	    &amp;quot;notificationGroupName&amp;quot;: {&lt;br /&gt;
	      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
	      requiredOnCreate: true,&lt;br /&gt;
	      canUpdate: true,&lt;br /&gt;
	      validation: {&lt;br /&gt;
	        pattern: pattern&lt;br /&gt;
	      }&lt;br /&gt;
	    },&lt;br /&gt;
	    &amp;quot;consolidatedType&amp;quot;: {&lt;br /&gt;
	      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
	      optionalOnCreate: true,&lt;br /&gt;
	      requiredOnCreate: false,&lt;br /&gt;
	      canUpdate: true,&lt;br /&gt;
	      validation: {&lt;br /&gt;
	        pattern: pattern&lt;br /&gt;
	      }&lt;br /&gt;
	    },&lt;br /&gt;
	    &amp;quot;consolidatedFrequency&amp;quot;: {&lt;br /&gt;
	      type: &amp;quot;number&amp;quot;,&lt;br /&gt;
	      optionalOnCreate: true,&lt;br /&gt;
	      requiredOnCreate: false,&lt;br /&gt;
	      canUpdate: true,&lt;br /&gt;
	      validation: {&lt;br /&gt;
	        pattern: pattern&lt;br /&gt;
	      }&lt;br /&gt;
	    },&lt;br /&gt;
	    &amp;quot;consolidatedSendIfEmpty&amp;quot;: {&lt;br /&gt;
	      type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
	      optionalOnCreate: true,&lt;br /&gt;
	      requiredOnCreate: false,&lt;br /&gt;
	      canUpdate: true,&lt;br /&gt;
	      validation: {&lt;br /&gt;
	        pattern: pattern&lt;br /&gt;
	      }&lt;br /&gt;
	    },&lt;br /&gt;
	    &amp;quot;consolidatedMessageCount&amp;quot;: {&lt;br /&gt;
	      type: &amp;quot;number&amp;quot;,&lt;br /&gt;
	      optionalOnCreate: true,&lt;br /&gt;
	      requiredOnCreate: false,&lt;br /&gt;
	      canUpdate: true,&lt;br /&gt;
	      validation: {&lt;br /&gt;
	        pattern: pattern&lt;br /&gt;
	      }&lt;br /&gt;
    	},&lt;br /&gt;
	    &amp;quot;consolidatedConfigs&amp;quot;: {&lt;br /&gt;
	      type: &amp;quot;array&amp;quot;,&lt;br /&gt;
    	  optionalOnCreate: true,&lt;br /&gt;
    	  requiredOnCreate: false,&lt;br /&gt;
    	  canUpdate: true,&lt;br /&gt;
	      validation: {&lt;br /&gt;
	        pattern: pattern&lt;br /&gt;
	      }&lt;br /&gt;
    	}&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  ],&lt;br /&gt;
  fieldNames: {&lt;br /&gt;
    notificationGroupId: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      randomOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['myGraph', &amp;quot;dynamoDB&amp;quot;]&lt;br /&gt;
    },&lt;br /&gt;
    consolidatedLastTimeSent: {&lt;br /&gt;
      type: &amp;quot;number&amp;quot;,&lt;br /&gt;
      optionalOnCreate: true,&lt;br /&gt;
      canUpdate: true,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: [&amp;quot;dynamoDB&amp;quot;]&lt;br /&gt;
    },&lt;br /&gt;
  },	&lt;br /&gt;
  identifiers: [&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
      fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (identifier)&lt;br /&gt;
: comes from: randomOnCreate&lt;br /&gt;
; consolidatedLastTimeSent&lt;br /&gt;
: start time for recurring or last time for send consolidated message&lt;br /&gt;
; notificationGroupName&lt;br /&gt;
: string name set by user&lt;br /&gt;
; consolidatedType&lt;br /&gt;
: none | recurring | messageCount&lt;br /&gt;
: ''none'' is not consolidated, process immediately&lt;br /&gt;
: ''recurring'' process at a specific frequency&lt;br /&gt;
: ''messageCount'' process when a specific number of messages have been received&lt;br /&gt;
; consolidatedFrequency&lt;br /&gt;
: for consolidatedType = recurring, number of milliseconds between each processing&lt;br /&gt;
; consolidatedSendIfEmpty&lt;br /&gt;
: setting for if have no consolidated message&lt;br /&gt;
: true = send message out&lt;br /&gt;
: false = not send message out&lt;br /&gt;
; consolidatedMessageCount&lt;br /&gt;
: send a consolidated message when the number of messages reaches&lt;br /&gt;
; consolidatedConfigs&lt;br /&gt;
: pattern for message to send data in consolidated message&lt;br /&gt;
&lt;br /&gt;
=== consolidated ===&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;					&lt;br /&gt;
{&lt;br /&gt;
  objectType: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
  canDelete: false,&lt;br /&gt;
  storageResources: {&lt;br /&gt;
    dynamoDB: {&lt;br /&gt;
      storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
      tableName: &amp;quot;PendingConsolidation&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  fieldNames: {&lt;br /&gt;
    notificationGroupId: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      storageResourceTags: ['dynamoDB'],&lt;br /&gt;
      fromObjType: {&lt;br /&gt;
	    serviceTag: &amp;quot;NotificationManager&amp;quot;,&lt;br /&gt;
	    objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    checkedTriggerTime: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      requiredOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['dynamoDB']&lt;br /&gt;
    },&lt;br /&gt;
    activityMsg: {&lt;br /&gt;
      type: &amp;quot;object&amp;quot;,&lt;br /&gt;
      requiredOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['dynamoDB']&lt;br /&gt;
    },&lt;br /&gt;
    flowTag: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      requiredOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['dynamoDB']&lt;br /&gt;
    },&lt;br /&gt;
    flowStep: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      requiredOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['dynamoDB']&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  identifiers: [&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
      fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
      fieldName: &amp;quot;checkedTriggerTime&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: comes from: randomOnCreate&lt;br /&gt;
; checkedTriggerTime (sort key)&lt;br /&gt;
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId&lt;br /&gt;
; activityMsg&lt;br /&gt;
: the message delivered from Activity Switchboard&lt;br /&gt;
; flowTag&lt;br /&gt;
: defines the consolidated configuration for the notification group&lt;br /&gt;
; flowType&lt;br /&gt;
: defines the consolidated configuration for the notification group&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== ownsNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ownsNotificationGroup&amp;quot;: {&lt;br /&gt;
     fieldNames: {&lt;br /&gt;
       &amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
	     type: &amp;quot;number&amp;quot;,&lt;br /&gt;
	     requiredOnCreate: true,  	// default = false&lt;br /&gt;
	    canUpdate: true,          	// default = true&lt;br /&gt;
	    validation: {}            	// ajv syntax&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
      myGraph: {&lt;br /&gt;
	    storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
	    graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    links: [&lt;br /&gt;
      {&lt;br /&gt;
	    storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
	    from: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;UserAccount&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
	    },&lt;br /&gt;
	    to: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;NotificationManager&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      requiredOnCreate: true,&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
	      handler: true&lt;br /&gt;
	    }&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: links the user to their created notification group&lt;br /&gt;
&lt;br /&gt;
=== hasTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;hasTriggerGroup&amp;quot;: {&lt;br /&gt;
    canChangeToRelTypes: [&lt;br /&gt;
      {&lt;br /&gt;
	    serviceTag: &amp;quot;NotificationManager&amp;quot;,&lt;br /&gt;
	    relationshipTag: &amp;quot;disabledTriggerGroup&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
      &amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
	    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
	    requiredOnCreate: true,  	// default = false&lt;br /&gt;
	    canUpdate: true,          	// default = true&lt;br /&gt;
	    validation: {}            	// ajv syntax&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
      myGraph: {&lt;br /&gt;
	    storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
	    graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    links: [&lt;br /&gt;
      {&lt;br /&gt;
	    storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
	    from: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;NotificationManager&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
    	  },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
	      handler: true&lt;br /&gt;
    	},&lt;br /&gt;
	    to: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
    	}&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;disabledTriggerGroup&amp;quot;: {&lt;br /&gt;
    canChangeToRelTypes: [&lt;br /&gt;
      {&lt;br /&gt;
	    serviceTag: &amp;quot;NotificationManager&amp;quot;,&lt;br /&gt;
	    relationshipTag: &amp;quot;hasTriggerGroup&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
      &amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
	    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
	    requiredOnCreate: true,  	// default = false&lt;br /&gt;
	    canUpdate: true,          	// default = true&lt;br /&gt;
	    validation: {}            	// ajv syntax&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
      myGraph: {&lt;br /&gt;
	    storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
	    graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    links: [&lt;br /&gt;
      {&lt;br /&gt;
	    storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
	    from: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;NotificationManager&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
	      handler: true&lt;br /&gt;
	    },&lt;br /&gt;
	    to: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
    	}&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= value or values array =&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName &lt;br /&gt;
&lt;br /&gt;
= serviceName and topicName triggers =&lt;br /&gt;
&lt;br /&gt;
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass&lt;br /&gt;
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes&lt;br /&gt;
* Can set serviceName+topicName, then only messages in that topic will pass&lt;br /&gt;
* serviceName and topicName can both be submitted as &amp;quot;values&amp;quot; array, creating multiple trigger groups for each combination&lt;br /&gt;
&lt;br /&gt;
= Consolidated notifications =&lt;br /&gt;
&lt;br /&gt;
== How to trigger processing for time based consolidations == &lt;br /&gt;
&lt;br /&gt;
* ....&lt;br /&gt;
* 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)&lt;br /&gt;
&lt;br /&gt;
= Formating the notification body = &lt;br /&gt;
&lt;br /&gt;
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body&lt;br /&gt;
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs&lt;br /&gt;
&lt;br /&gt;
= Ideas = &lt;br /&gt;
&lt;br /&gt;
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level&lt;br /&gt;
* 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&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Notification Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=3551</id>
		<title>Service - Activity Switchboard</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=3551"/>
		<updated>2025-09-11T04:19:10Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Receives most messages sent within the entire project's serverless flow, allows services to register a set of triggers that are checked when each message is received, if all triggers pass a message is sent out for subscribed services.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-activity-switchboard&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== triggerGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggers, all triggers for a trigger group must pass for the trigger group to pass&lt;br /&gt;
* if multiple values are sent for a trigger we consider this an &amp;quot;or&amp;quot; check, any of the values can match. We do this by saving each value as a separate trigger, then when checking a trigger groups child triggers we have a counter that enables a check if any one of the values matches&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  objectType: &amp;quot;triggerGroup&amp;quot;,&lt;br /&gt;
  canDelete: false,&lt;br /&gt;
  storageResources: {&lt;br /&gt;
    myGraph: {&lt;br /&gt;
      storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
      graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  fieldNames: {&lt;br /&gt;
    triggerGroupId: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      randomOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['myGraph']&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  identifiers: [&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
      fieldName: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== trigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  objectType: &amp;quot;trigger&amp;quot;,&lt;br /&gt;
  canDelete: false,&lt;br /&gt;
  storageResources: {&lt;br /&gt;
    dynamoDB: {&lt;br /&gt;
      storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
      tableName: &amp;quot;Triggers&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  fieldNames: {&lt;br /&gt;
    triggerId: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      randomOnCreate: false,&lt;br /&gt;
      optionalOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['dynamoDB']&lt;br /&gt;
    },&lt;br /&gt;
    valueType: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      requiredOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['dynamoDB']&lt;br /&gt;
    },&lt;br /&gt;
    propertyValueString: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      optionalOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['dynamoDB']&lt;br /&gt;
    },&lt;br /&gt;
    propertyValueNumber: {&lt;br /&gt;
      type: &amp;quot;number&amp;quot;,&lt;br /&gt;
      optionalOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {&lt;br /&gt;
	    pattern: pattern&lt;br /&gt;
      },&lt;br /&gt;
      storageResourceTags: ['dynamoDB']&lt;br /&gt;
    },&lt;br /&gt;
    propertyName: {&lt;br /&gt;
      type: &amp;quot;string&amp;quot;,&lt;br /&gt;
      optionalOnCreate: true,&lt;br /&gt;
      canUpdate: false,&lt;br /&gt;
      validation: {},&lt;br /&gt;
      storageResourceTags: ['dynamoDB']&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  identifiers: [&lt;br /&gt;
    {&lt;br /&gt;
      type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
      fieldName: &amp;quot;triggerId&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerId&lt;br /&gt;
: (identifier)&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue (string or number)}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
: attributes is for message attributes&lt;br /&gt;
: property is from the data sent inside the message body&lt;br /&gt;
: hash property name and value so no ambiguity about underscores/spaces etc..&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC (not validate nested object)&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: for value of propertyValue is string&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: for value of propertyValue is number&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== hasTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;hasTrigger&amp;quot;: {&lt;br /&gt;
    canChangeToRelTypes: [&lt;br /&gt;
      {&lt;br /&gt;
	    serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,&lt;br /&gt;
	    relationshipTag: &amp;quot;disabledTrigger&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
      &amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
	    type: &amp;quot;number&amp;quot;,  			&lt;br /&gt;
	    requiredOnCreate: true,  	// default = false&lt;br /&gt;
  	    canUpdate: true,          	// default = true&lt;br /&gt;
	    validation: {}            	// ajv syntax&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
      dynamo: {&lt;br /&gt;
	    storageType: &amp;quot;dynamoDB&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    links: [&lt;br /&gt;
      {&lt;br /&gt;
	    storageResourceTags: [&amp;quot;dynamo&amp;quot;],&lt;br /&gt;
	    from: {&lt;br /&gt;
 	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,	&lt;br /&gt;
	        objectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
	      handler: true		&lt;br /&gt;
	    },&lt;br /&gt;
	    to: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,	&lt;br /&gt;
	        objectType: &amp;quot;trigger&amp;quot;		&lt;br /&gt;
	      },				&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
	    }&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;disabledTrigger&amp;quot;: {&lt;br /&gt;
    canChangeToRelTypes: [&lt;br /&gt;
      {&lt;br /&gt;
	    serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,&lt;br /&gt;
	    relationshipTag: &amp;quot;hasTrigger&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
      &amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
	    type: &amp;quot;number&amp;quot;,&lt;br /&gt;
	    requiredOnCreate: true,  	// default = false&lt;br /&gt;
	    canUpdate: true,          	// default = true&lt;br /&gt;
	    validation: {}            	// ajv syntax&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
      dynamo: {&lt;br /&gt;
	    storageType: &amp;quot;dynamoDB&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    links: [&lt;br /&gt;
      {&lt;br /&gt;
	    storageResourceTags: [&amp;quot;dynamo&amp;quot;],&lt;br /&gt;
	    from: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,&lt;br /&gt;
	        objectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
	      handler: true&lt;br /&gt;
	    },&lt;br /&gt;
	    to: {&lt;br /&gt;
	      objType: {&lt;br /&gt;
	        serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,	&lt;br /&gt;
 	        objectType: &amp;quot;trigger&amp;quot;	&lt;br /&gt;
	      },&lt;br /&gt;
	      linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
	    }&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Efficiency =&lt;br /&gt;
&lt;br /&gt;
* Service will result in a large number of queries to Triggers table, every message will need to make a query for every field set as an activityTrigger&lt;br /&gt;
* Try to make this as efficient as possible&lt;br /&gt;
* To reduce number of queries made to Tiggers table we use the msgCfg for any message received&lt;br /&gt;
** MsgCfg sets which properties can be used as triggers, others are not queried&lt;br /&gt;
&lt;br /&gt;
= Handling msgCfgs =&lt;br /&gt;
&lt;br /&gt;
* msgCfgs get updated from [[Service - Message Config Manager|Message Config Manager]] service, we do this by subscribing to Message Config Manager's OutMsgCfgUpdate topic&lt;br /&gt;
* Only want to process messages from certain In/Out topics, it will be most topics but does not have to be all&lt;br /&gt;
* Set which topics to subscribe to by setting activitySwitchboard = true in msgCfg&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
* If more efficient can use cache for regular DynamoDB queries&lt;br /&gt;
* Could add other matching methods such as greater than or less than, in DynamoDB this might be more efficient to add as separate table with its own structure, eg: partition key is the field reference, sort key is the amount, then use sort key to return matching triggers. Danger of bad partitioning in DynamoDB (or hitting limits) due to large numbers of sort keys associated with one partition key&lt;br /&gt;
* could consider how to incorporate includes or checking within a set of options set in the trigger, again might need specialized table structure&lt;br /&gt;
* Might be able to optimise by using SNS &amp;gt; stream instead of SNS &amp;gt; SQS for all incomming messages&lt;br /&gt;
* One system level receiving service could be specialized logs, eg logs per service per user/per product/etc&lt;br /&gt;
* Topic name is not fixed part of the TriggersGroup structure, allow for trigger groups that are not connected to specific topics, but can pass messages from any topic that matches other triggers&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Activity Switchboard|Working_documents - Activity Switchboard]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Activity Switchboard]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=3301</id>
		<title>Service - Notification Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=3301"/>
		<updated>2025-06-23T02:06:05Z</updated>

		<summary type="html">&lt;p&gt;Pong: /* objType */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-notification-manager&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== notificationGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggerGroups together&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationGroup&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationGroupSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationGroupName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedType&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedFrequency&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedNextSendDue&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;number&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedSendIfEmpty&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (identifier)&lt;br /&gt;
: comes from: hash{notifications}&lt;br /&gt;
; notificationGroupName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; consolidatedType&lt;br /&gt;
: none | recurring | messageCount&lt;br /&gt;
: ''none'' is not consolidated, process immediately&lt;br /&gt;
: ''recurring'' process at a specific frequency&lt;br /&gt;
: ''messageCount'' process when a specific number of messages have been received&lt;br /&gt;
; consolidatedFrequency&lt;br /&gt;
: for consolidatedType = recurring, number of seconds between each processing&lt;br /&gt;
; consolidatedNextSendDue&lt;br /&gt;
: timestamp for next due time to send notification&lt;br /&gt;
; consolidatedSendIfEmpty&lt;br /&gt;
: true | false&lt;br /&gt;
&lt;br /&gt;
=== consolidated ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [],      // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 		 // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;PendingConsolidation&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;NotificationManager&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		time: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		activityMsg: {&lt;br /&gt;
			type: &amp;quot;object&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;time&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: comes from: hash{notifications}&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId&lt;br /&gt;
; activityMsg&lt;br /&gt;
: copy of the message delivered from Activity Switchboard&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== ownsNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;ownsNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserAccount&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: links user to notificationGroup that user created &lt;br /&gt;
&lt;br /&gt;
=== hasNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== hasTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasTriggerGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;					&lt;br /&gt;
					},				&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledTriggerGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= value or values array =&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName &lt;br /&gt;
&lt;br /&gt;
= serviceName and topicName triggers =&lt;br /&gt;
&lt;br /&gt;
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass&lt;br /&gt;
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes&lt;br /&gt;
* Can set serviceName+topicName, then only messages in that topic will pass&lt;br /&gt;
* serviceName and topicName can both be submitted as &amp;quot;values&amp;quot; array, creating multiple trigger groups for each combination&lt;br /&gt;
&lt;br /&gt;
= Consolidated notifications =&lt;br /&gt;
&lt;br /&gt;
== How to trigger processing for time based consolidations == &lt;br /&gt;
&lt;br /&gt;
* ....&lt;br /&gt;
* 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)&lt;br /&gt;
&lt;br /&gt;
= Formating the notification body = &lt;br /&gt;
&lt;br /&gt;
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body&lt;br /&gt;
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs&lt;br /&gt;
&lt;br /&gt;
= Ideas = &lt;br /&gt;
&lt;br /&gt;
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level&lt;br /&gt;
* 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&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Notification Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2025-06-21_-_Consolidated_notification_messages&amp;diff=3299</id>
		<title>2025-06-21 - Consolidated notification messages</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2025-06-21_-_Consolidated_notification_messages&amp;diff=3299"/>
		<updated>2025-06-23T01:49:00Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Search Results]]&lt;br /&gt;
&lt;br /&gt;
= Triggering processing of consolidated notifications =&lt;br /&gt;
* Eventbridge for one off events&lt;br /&gt;
* Use for notificationGroups that have a recurring schedule&lt;br /&gt;
* Not for notificationGroups that consolidate when x number of messages received&lt;br /&gt;
* When a recurring group is processed, add it's schedule to the old schedule timeframe so that the time processed is standard (eg processes every day at the same time)&lt;br /&gt;
&lt;br /&gt;
= Processing notificationGroups after x number of messages =&lt;br /&gt;
* This is handled each time a message comes in, count the number of consolidated notifications waiting to send&lt;br /&gt;
&lt;br /&gt;
= Limits =&lt;br /&gt;
* Maybe hardcode a const max frequency or number of consolidated messages a group can handle, perhaps deactivating groups that process too many&lt;br /&gt;
* Hardcode a max number of consolidated messages to list in one message&lt;br /&gt;
&lt;br /&gt;
= Message content =&lt;br /&gt;
* triggers match to flowSteps so we know the possible params of each message&lt;br /&gt;
* Each notificationGroup sets up for each message type whether to show aggregate or detailed list of the messages (can show both)&lt;br /&gt;
* Must show something for each triggerGroup&lt;br /&gt;
* Both aggregate and detailed can choose which params to show&lt;br /&gt;
* params can be an array which orders the params in the message output&lt;br /&gt;
* add these settings to ?versionedData&lt;br /&gt;
 &lt;br /&gt;
== example consolidatedConfig ==&lt;br /&gt;
&lt;br /&gt;
* in notificationGroup versionData&lt;br /&gt;
* each consolidated records it's triggerGroupId&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
      {&lt;br /&gt;
	      triggerGroupId: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	      flowStepPropertyName: &amp;quot;xxx&amp;quot;&lt;br /&gt;
	      aggregate: &amp;quot;sum&amp;quot;, //sum|count|max|average|...&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot; // user defined label for result&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
	      triggerGroupId: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	      flowStepProperties: [&lt;br /&gt;
		    {&lt;br /&gt;
			flowStepPropertyName: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
			label: &amp;quot;qqq&amp;quot;&lt;br /&gt;
		    },&lt;br /&gt;
		    {&lt;br /&gt;
			flowStepPropertyName: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
			label: &amp;quot;rrr&amp;quot;&lt;br /&gt;
		    },&lt;br /&gt;
		    // ... &lt;br /&gt;
	      ]&lt;br /&gt;
	      // no aggregate means detailed list of messages&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot; // user defined label for result&lt;br /&gt;
	      showTimestamps: true&lt;br /&gt;
	},&lt;br /&gt;
	// ...&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== example data sent to ContactManager ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
      {&lt;br /&gt;
	      triggerGroupId: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	      flowStepPropertyName: &amp;quot;xxx&amp;quot;&lt;br /&gt;
	      aggregate: &amp;quot;sum&amp;quot;,&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot;,&lt;br /&gt;
	      value: &amp;quot;zzzz&amp;quot; // calculated aggregate value&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
	      triggerGroupId: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	      values: [ // array of messages&lt;br /&gt;
		{&lt;br /&gt;
		      timestamp: &amp;quot;zzz&amp;quot;,&lt;br /&gt;
		      flowStepProperties: [&lt;br /&gt;
			    {&lt;br /&gt;
				  flowStepPropertyName: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
				  label: &amp;quot;qqq&amp;quot;,&lt;br /&gt;
				  value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
			    },&lt;br /&gt;
			    {&lt;br /&gt;
				  flowStepPropertyName: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
				  label: &amp;quot;rrr&amp;quot;,&lt;br /&gt;
				  value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
			    },&lt;br /&gt;
			    // ..&lt;br /&gt;
		},&lt;br /&gt;
		// ...&lt;br /&gt;
	      ],&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot;, // user defined label for result&lt;br /&gt;
	      showTimestamps: true&lt;br /&gt;
	},&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
* consolidated maybe have ttl setting to auto remove? Or could clean up each time a group is processed&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2025-06-21]]&lt;br /&gt;
[[Category:Working documents - Notification Manager| 2025-06-21]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2025-06-21_-_Consolidated_notification_messages&amp;diff=3298</id>
		<title>2025-06-21 - Consolidated notification messages</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2025-06-21_-_Consolidated_notification_messages&amp;diff=3298"/>
		<updated>2025-06-23T01:48:27Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Search Results]]&lt;br /&gt;
&lt;br /&gt;
= Triggering processing of consolidated notifications =&lt;br /&gt;
* Eventbridge for one off events&lt;br /&gt;
* Use for notificationGroups that have a recurring schedule&lt;br /&gt;
* Not for notificationGroups that consolidate when x number of messages received&lt;br /&gt;
* When a recurring group is processed, add it's schedule to the old schedule timeframe so that the time processed is standard (eg processes every day at the same time)&lt;br /&gt;
&lt;br /&gt;
= Processing notificationGroups after x number of messages =&lt;br /&gt;
* This is handled each time a message comes in, count the number of consolidated notifications waiting to send&lt;br /&gt;
&lt;br /&gt;
= Limits =&lt;br /&gt;
* Maybe hardcode a const max frequency or number of consolidated messages a group can handle, perhaps deactivating groups that process too many&lt;br /&gt;
* Hardcode a max number of consolidated messages to list in one message&lt;br /&gt;
&lt;br /&gt;
= Message content =&lt;br /&gt;
* triggers match to flowSteps so we know the possible params of each message&lt;br /&gt;
* Each notificationGroup sets up for each message type whether to show aggregate or detailed list of the messages (can show both)&lt;br /&gt;
* Must show something for each triggerGroup&lt;br /&gt;
* Both aggregate and detailed can choose which params to show&lt;br /&gt;
* params can be an array which orders the params in the message output&lt;br /&gt;
* add these settings to ?versionedData&lt;br /&gt;
 &lt;br /&gt;
== example consolidatedConfig ==&lt;br /&gt;
&lt;br /&gt;
* in notificationGroup versionData&lt;br /&gt;
* each consolidated records it's triggerGroupId&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
      {&lt;br /&gt;
	      triggerGroupId: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	      flowStepPropertyName: &amp;quot;xxx&amp;quot;&lt;br /&gt;
	      aggregate: &amp;quot;sum&amp;quot;, //sum|count|max|average|...&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot; // user defined label for result&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
	      triggerGroupId: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	      flowStepProperties: [&lt;br /&gt;
		    {&lt;br /&gt;
			flowStepPropertyName: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
			label: &amp;quot;qqq&amp;quot;&lt;br /&gt;
		    },&lt;br /&gt;
		    {&lt;br /&gt;
			flowStepPropertyName: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
			label: &amp;quot;rrr&amp;quot;&lt;br /&gt;
		    },&lt;br /&gt;
		    // ... &lt;br /&gt;
	      ]&lt;br /&gt;
	      // no aggregate means detailed list of messages&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot; // user defined label for result&lt;br /&gt;
	      showTimestamps: true&lt;br /&gt;
	},&lt;br /&gt;
	// ...&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== example data sent to ContactManager ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
[&lt;br /&gt;
      {&lt;br /&gt;
	      triggerGroupId: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	      flowStepPropertyName: &amp;quot;xxx&amp;quot;&lt;br /&gt;
	      aggregate: &amp;quot;sum&amp;quot;,&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot;,&lt;br /&gt;
	      value: &amp;quot;zzzz&amp;quot; // calculated aggregate value&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
	      triggerGroupId: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
	      values: [ // array of messages&lt;br /&gt;
		{&lt;br /&gt;
		      timestamp: &amp;quot;zzz&amp;quot;,&lt;br /&gt;
		      flowStepProperties: [&lt;br /&gt;
			    {&lt;br /&gt;
				  flowStepPropertyName: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
				  label: &amp;quot;qqq&amp;quot;,&lt;br /&gt;
				  value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
			    },&lt;br /&gt;
			    {&lt;br /&gt;
				  flowStepPropertyName: &amp;quot;xxx&amp;quot;,&lt;br /&gt;
				  label: &amp;quot;rrr&amp;quot;,&lt;br /&gt;
				  value: &amp;quot;zzzz&amp;quot;&lt;br /&gt;
			    },&lt;br /&gt;
			    // ..&lt;br /&gt;
		},&lt;br /&gt;
		// ...&lt;br /&gt;
	      ],&lt;br /&gt;
	      label: &amp;quot;qqq&amp;quot;, // user defined label for result&lt;br /&gt;
	      showTimestamps: true&lt;br /&gt;
	},&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 = Notes =&lt;br /&gt;
* consolidated maybe have ttl setting to auto remove? Or could clean up each time a group is processed&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2025-06-21]]&lt;br /&gt;
[[Category:Working documents - Notification Manager| 2025-06-21]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=3297</id>
		<title>Service - Notification Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=3297"/>
		<updated>2025-06-23T01:30:08Z</updated>

		<summary type="html">&lt;p&gt;Pong: /* consolidated */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-notification-manager&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== notificationGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggerGroups together&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationGroup&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationGroupSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationGroupName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedType&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedFrequency&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedNextSendDue&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;number&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedSendIfEmpty&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (identifier)&lt;br /&gt;
: comes from: hash{notifications}&lt;br /&gt;
; notificationGroupName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; consolidated (maybe remove and use '''consolidatedType''' for check consolidated )&lt;br /&gt;
: true | false&lt;br /&gt;
; consolidatedType&lt;br /&gt;
: overview | detailed | null&lt;br /&gt;
: ''overview'' sends a count of each type of notification, ''detailed'' lists each activity message&lt;br /&gt;
: ''null'' that mean notificationGroup is not consolidated and send to receiver service&lt;br /&gt;
; consolidatedFrequency&lt;br /&gt;
: not sure, maybe use some sort of standard like cron&lt;br /&gt;
; consolidatedNextSendDue&lt;br /&gt;
: timestamp for next due time to send notification&lt;br /&gt;
; consolidatedSendIfEmpty&lt;br /&gt;
: true | false&lt;br /&gt;
&lt;br /&gt;
=== consolidated ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [],      // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 		 // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;PendingConsolidation&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;NotificationManager&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		time: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		activityMsg: {&lt;br /&gt;
			type: &amp;quot;object&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;time&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: comes from: hash{notifications}&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId&lt;br /&gt;
; activityMsg&lt;br /&gt;
: copy of the message delivered from Activity Switchboard&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== ownsNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;ownsNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserAccount&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: links user to notificationGroup that user created &lt;br /&gt;
&lt;br /&gt;
=== hasNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== hasTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasTriggerGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;					&lt;br /&gt;
					},				&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledTriggerGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= value or values array =&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName &lt;br /&gt;
&lt;br /&gt;
= serviceName and topicName triggers =&lt;br /&gt;
&lt;br /&gt;
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass&lt;br /&gt;
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes&lt;br /&gt;
* Can set serviceName+topicName, then only messages in that topic will pass&lt;br /&gt;
* serviceName and topicName can both be submitted as &amp;quot;values&amp;quot; array, creating multiple trigger groups for each combination&lt;br /&gt;
&lt;br /&gt;
= Consolidated notifications =&lt;br /&gt;
&lt;br /&gt;
== How to trigger processing for time based consolidations == &lt;br /&gt;
&lt;br /&gt;
* ....&lt;br /&gt;
* 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)&lt;br /&gt;
&lt;br /&gt;
= Formating the notification body = &lt;br /&gt;
&lt;br /&gt;
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body&lt;br /&gt;
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs&lt;br /&gt;
&lt;br /&gt;
= Ideas = &lt;br /&gt;
&lt;br /&gt;
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level&lt;br /&gt;
* 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&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Notification Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2975</id>
		<title>Service - Notification Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2975"/>
		<updated>2025-03-19T06:27:19Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-notification-manager&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== notificationGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggerGroups together&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationGroup&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationGroupSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationGroupName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedType&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedFrequency&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedNextSendDue&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;number&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedSendIfEmpty&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (identifier)&lt;br /&gt;
: comes from: hash{notifications}&lt;br /&gt;
; notificationGroupName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; consolidated (maybe remove and use '''consolidatedType''' for check consolidated )&lt;br /&gt;
: true | false&lt;br /&gt;
; consolidatedType&lt;br /&gt;
: overview | detailed | null&lt;br /&gt;
: ''overview'' sends a count of each type of notification, ''detailed'' lists each activity message&lt;br /&gt;
: ''null'' that mean notificationGroup is not consolidated and send to receiver service&lt;br /&gt;
; consolidatedFrequency&lt;br /&gt;
: not sure, maybe use some sort of standard like cron&lt;br /&gt;
; consolidatedNextSendDue&lt;br /&gt;
: timestamp for next due time to send notification&lt;br /&gt;
; consolidatedSendIfEmpty&lt;br /&gt;
: true | false&lt;br /&gt;
&lt;br /&gt;
=== consolidated ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [],      // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 		 // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;PendingConsolidation&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;NotificationManager&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		time: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		activityMsg: {&lt;br /&gt;
			type: &amp;quot;object&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;time&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: comes from: hash{notifications}&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId&lt;br /&gt;
; activityMsg&lt;br /&gt;
: copy of the message delivered from Activity Switchboard&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== ownsNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;ownsNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserAccount&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: links user to notificationGroup that user created &lt;br /&gt;
&lt;br /&gt;
=== hasNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== hasTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasTriggerGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;					&lt;br /&gt;
					},				&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledTriggerGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= value or values array =&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName &lt;br /&gt;
&lt;br /&gt;
= serviceName and topicName triggers =&lt;br /&gt;
&lt;br /&gt;
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass&lt;br /&gt;
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes&lt;br /&gt;
* Can set serviceName+topicName, then only messages in that topic will pass&lt;br /&gt;
* serviceName and topicName can both be submitted as &amp;quot;values&amp;quot; array, creating multiple trigger groups for each combination&lt;br /&gt;
&lt;br /&gt;
= Consolidated notifications =&lt;br /&gt;
&lt;br /&gt;
== How to trigger processing for time based consolidations == &lt;br /&gt;
&lt;br /&gt;
* ....&lt;br /&gt;
* 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)&lt;br /&gt;
&lt;br /&gt;
= Formating the notification body = &lt;br /&gt;
&lt;br /&gt;
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body&lt;br /&gt;
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs&lt;br /&gt;
&lt;br /&gt;
= Ideas = &lt;br /&gt;
&lt;br /&gt;
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level&lt;br /&gt;
* 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&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Notification Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2974</id>
		<title>Service - Activity Switchboard</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2974"/>
		<updated>2025-03-19T06:13:29Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Receives most messages sent within the entire project's serverless flow, allows services to register a set of triggers that are checked when each message is received, if all triggers pass a message is sent out for subscribed services.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-activity-switchboard&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== triggerGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggers, all triggers for a trigger group must pass for the trigger group to pass&lt;br /&gt;
* if multiple values are sent for a trigger we consider this an &amp;quot;or&amp;quot; check, any of the values can match. We do this by saving each value as a separate trigger, then when checking a trigger groups child triggers we have a counter that enables a check if any one of the values matches&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;triggerGroup&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;, &amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: comes from: hash{triggers}&lt;br /&gt;
&lt;br /&gt;
=== trigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;trigger&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;, &amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;Triggers&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB', 'myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerId&lt;br /&gt;
: (identifier)&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
: attributes is for message attributes&lt;br /&gt;
: property is from the data sent inside the message body&lt;br /&gt;
: hash property name and value so no ambiguity about underscores/spaces etc..&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== hasTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasTrigger&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;trigger&amp;quot;					&lt;br /&gt;
					},				&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledTrigger&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;trigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Efficiency =&lt;br /&gt;
&lt;br /&gt;
* Service will result in a large number of queries to Triggers table, every message will need to make a query for every field set as an activityTrigger&lt;br /&gt;
* Try to make this as efficient as possible&lt;br /&gt;
* To reduce number of queries made to Tiggers table we use the msgCfg for any message received&lt;br /&gt;
** MsgCfg sets which properties can be used as triggers, others are not queried&lt;br /&gt;
&lt;br /&gt;
= Handling msgCfgs =&lt;br /&gt;
&lt;br /&gt;
* msgCfgs get updated from [[Service - Message Config Manager|Message Config Manager]] service, we do this by subscribing to Message Config Manager's OutMsgCfgUpdate topic&lt;br /&gt;
* Only want to process messages from certain In/Out topics, it will be most topics but does not have to be all&lt;br /&gt;
* Set which topics to subscribe to by setting activitySwitchboard = true in msgCfg&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
* If more efficient can use cache for regular DynamoDB queries&lt;br /&gt;
* Could add other matching methods such as greater than or less than, in DynamoDB this might be more efficient to add as separate table with its own structure, eg: partition key is the field reference, sort key is the amount, then use sort key to return matching triggers. Danger of bad partitioning in DynamoDB (or hitting limits) due to large numbers of sort keys associated with one partition key&lt;br /&gt;
* could consider how to incorporate includes or checking within a set of options set in the trigger, again might need specialized table structure&lt;br /&gt;
* Might be able to optimise by using SNS &amp;gt; stream instead of SNS &amp;gt; SQS for all incomming messages&lt;br /&gt;
* One system level receiving service could be specialized logs, eg logs per service per user/per product/etc&lt;br /&gt;
* Topic name is not fixed part of the TriggersGroup structure, allow for trigger groups that are not connected to specific topics, but can pass messages from any topic that matches other triggers&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Activity Switchboard|Working_documents - Activity Switchboard]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Activity Switchboard]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2962</id>
		<title>Service - Notification Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2962"/>
		<updated>2025-03-17T09:10:56Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-notification-manager&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== PendingConsolidation Table ==&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: type: string&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: type: number&lt;br /&gt;
: {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
; activityMsg&lt;br /&gt;
: type: object&lt;br /&gt;
: object containing ''messageAttributes'' and ''message''&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== notificationGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many notifications together&lt;br /&gt;
* Simplifies management of similar notifications&lt;br /&gt;
* Allows for consolidation of notifications at notification group level&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationGroup&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationGroupSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationGroupName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{ &lt;br /&gt;
					fieldName: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedType&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedFrequency&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedNextSendDue&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;number&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedSendIfEmpty&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; notificationGroupName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; consolidated&lt;br /&gt;
: true|false&lt;br /&gt;
; consolidatedType&lt;br /&gt;
: overview|detailed&lt;br /&gt;
: ''overview'' sends a count of each type of notification, ''detailed'' lists each activity message&lt;br /&gt;
; consolidatedFrequency&lt;br /&gt;
: not sure, maybe use some sort of standard like cron&lt;br /&gt;
; consolidatedNextSendDue&lt;br /&gt;
: timestamp for next due time to send notification&lt;br /&gt;
; consolidatedSendIfEmpty&lt;br /&gt;
: true|false&lt;br /&gt;
&lt;br /&gt;
=== consolidated ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [],      // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 		 // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;PendingConsolidation&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;NotificationManager&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		time: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		activityMsg: {&lt;br /&gt;
			type: &amp;quot;object&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;time&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId&lt;br /&gt;
; activityMsg&lt;br /&gt;
: copy of the message delivered from Activity Switchboard&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== ownNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;ownNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: links userContact to notificationGroup that user created &lt;br /&gt;
&lt;br /&gt;
=== currentNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== hasTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasTriggerGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;					&lt;br /&gt;
					},				&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledTriggerGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledTriggerGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= value or values array =&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName &lt;br /&gt;
&lt;br /&gt;
= serviceName and topicName triggers =&lt;br /&gt;
&lt;br /&gt;
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass&lt;br /&gt;
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes&lt;br /&gt;
* Can set serviceName+topicName, then only messages in that topic will pass&lt;br /&gt;
* serviceName and topicName can both be submitted as &amp;quot;values&amp;quot; array, creating multiple trigger groups for each combination&lt;br /&gt;
&lt;br /&gt;
= Consolidated notifications =&lt;br /&gt;
&lt;br /&gt;
== How to trigger processing for time based consolidations == &lt;br /&gt;
&lt;br /&gt;
* ....&lt;br /&gt;
* 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)&lt;br /&gt;
&lt;br /&gt;
= Formating the notification body = &lt;br /&gt;
&lt;br /&gt;
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body&lt;br /&gt;
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs&lt;br /&gt;
&lt;br /&gt;
= Ideas = &lt;br /&gt;
&lt;br /&gt;
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level&lt;br /&gt;
* 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&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Notification Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2961</id>
		<title>Service - Activity Switchboard</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2961"/>
		<updated>2025-03-17T09:10:43Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Receives most messages sent within the entire project's serverless flow, allows services to register a set of triggers that are checked when each message is received, if all triggers pass a message is sent out for subscribed services.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-activity-switchboard&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== Triggers ==&lt;br /&gt;
&lt;br /&gt;
; triggerId (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
; valueType&lt;br /&gt;
: string &lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== triggerGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggers, all triggers for a trigger group must pass for the trigger group to pass&lt;br /&gt;
* if multiple values are sent for a trigger we consider this an &amp;quot;or&amp;quot; check, any of the values can match. We do this by saving each value as a separate trigger, then when checking a trigger groups child triggers we have a counter that enables a check if any one of the values matches&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;triggerGroup&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;, &amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		triggers: {&lt;br /&gt;
			type: &amp;quot;array&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: comes from: hash{triggers}&lt;br /&gt;
; triggers&lt;br /&gt;
: array of objects (DynamoDB list?)&lt;br /&gt;
: each element has triggerId, valueType, propertyName,  propertyValueString and propertyValueNumber&lt;br /&gt;
&lt;br /&gt;
=== trigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;trigger&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;, &amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;Triggers&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB', 'myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
: attributes is for message attributes&lt;br /&gt;
: property is from the data sent inside the message body&lt;br /&gt;
: hash property name and value so no ambiguity about underscores/spaces etc..&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
=== hasTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasTrigger&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;trigger&amp;quot;					&lt;br /&gt;
					},				&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledTrigger&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ActivitySwitchboard&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;trigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Efficiency =&lt;br /&gt;
&lt;br /&gt;
* Service will result in a large number of queries to Triggers table, every message will need to make a query for every field set as an activityTrigger&lt;br /&gt;
* Try to make this as efficient as possible&lt;br /&gt;
* To reduce number of queries made to Tiggers table we use the msgCfg for any message received&lt;br /&gt;
** MsgCfg sets which properties can be used as triggers, others are not queried&lt;br /&gt;
&lt;br /&gt;
= Handling msgCfgs =&lt;br /&gt;
&lt;br /&gt;
* msgCfgs get updated from [[Service - Message Config Manager|Message Config Manager]] service, we do this by subscribing to Message Config Manager's OutMsgCfgUpdate topic&lt;br /&gt;
* Only want to process messages from certain In/Out topics, it will be most topics but does not have to be all&lt;br /&gt;
* Set which topics to subscribe to by setting activitySwitchboard = true in msgCfg&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
* If more efficient can use cache for regular DynamoDB queries&lt;br /&gt;
* Could add other matching methods such as greater than or less than, in DynamoDB this might be more efficient to add as separate table with its own structure, eg: partition key is the field reference, sort key is the amount, then use sort key to return matching triggers. Danger of bad partitioning in DynamoDB (or hitting limits) due to large numbers of sort keys associated with one partition key&lt;br /&gt;
* could consider how to incorporate includes or checking within a set of options set in the trigger, again might need specialized table structure&lt;br /&gt;
* Might be able to optimise by using SNS &amp;gt; stream instead of SNS &amp;gt; SQS for all incomming messages&lt;br /&gt;
* One system level receiving service could be specialized logs, eg logs per service per user/per product/etc&lt;br /&gt;
* Topic name is not fixed part of the TriggersGroup structure, allow for trigger groups that are not connected to specific topics, but can pass messages from any topic that matches other triggers&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Activity Switchboard|Working_documents - Activity Switchboard]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Activity Switchboard]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=2946</id>
		<title>Service - User Contact Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=2946"/>
		<updated>2025-03-13T06:39:08Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Manages contact methods available to users. Records each users contacts.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-user-contact-manager&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== userContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;userContact&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				        // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			        // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;userContactSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
					fieldName: &amp;quot;contactTag&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		userContactId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		methodTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			name: &amp;quot;userContactId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; userContactId&lt;br /&gt;
: comes from random UUID&lt;br /&gt;
; methodTag&lt;br /&gt;
: comes from {serviceName} of receiver service &lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== hasUserContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasUserContact&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserAccount&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledUserContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledUserContact&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserAccount&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Creating a new user contact =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of available contact methods&lt;br /&gt;
* User choses one and and is directed to that contact method handler to create the actual entry&lt;br /&gt;
* Once the user contact is created in the handler service a message gets sent to this service which creates it's own UserContacts record&lt;br /&gt;
&lt;br /&gt;
= Creating a new notification group =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of the users contacts&lt;br /&gt;
* How do we handle authentication, notification manager has no record of the userId eg for notificationGroups, only UCM has this&lt;br /&gt;
* (for now not direct to notification manager, any updates go through UCM, but if can figure out some token system could go direct to notification manager) User choses one and is directed to [[Service - Notification Manager|Notification Manager]] service&lt;br /&gt;
* For the endpoint given to Notification Manager try having notifications go directly to the contact method handler. Another option would be to pass back through this service but I cannot see any benefit in this.&lt;br /&gt;
* Notification Manager completes the flow, creating a notification group and associated notification records&lt;br /&gt;
&lt;br /&gt;
= Location specific contact methods =&lt;br /&gt;
&lt;br /&gt;
* Could add ways of restricting the available contact methods to ones the user is likely to want&lt;br /&gt;
* eg: by location (eg country)&lt;br /&gt;
* but have a way that the user can choose any method&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
* If method handler changes the contactName, a message will be sent to User Contact Manager to update its record, contactName is part of the sort key, not sure if it can be updated, if not need to copy the record to a new record with the updated contactName&lt;br /&gt;
* If method handler disables a contact, disable here and send message to [[Service - Notification Manager|Notification Manager]] service which will disable notifications and tell [[Service - Activity Switchboard|Activity Switchboard]] service to remove all associated triggers&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - User Contact Manager|User Contact Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| User Contact Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Contact_Method_Email&amp;diff=2945</id>
		<title>Service - Contact Method Email</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Contact_Method_Email&amp;diff=2945"/>
		<updated>2025-03-11T02:26:53Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Manages email contact methods for users, details are not part of the public transparency followed across the majority of the project in order to protect against emails being scraped.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-contact-method-email&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== EmailAddress==&lt;br /&gt;
&lt;br /&gt;
=== Fields ===&lt;br /&gt;
&lt;br /&gt;
; userContactId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: a UUID generated when creating the email record, or sent by [[Service - User Contact Manager]]&lt;br /&gt;
; email&lt;br /&gt;
: string &lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== email ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;email&amp;quot;,&lt;br /&gt;
    extendObjType: { // core object that this object extends&lt;br /&gt;
      objectType: &amp;quot;userContact&amp;quot;,&lt;br /&gt;
      serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {&lt;br /&gt;
    	myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== emailAddress ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;emailAddress&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [],         // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 	        // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 		// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;, &amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;EmailAddress&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		userContactId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			// randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;UserContactManager&amp;quot; , // service responsible for object schema&lt;br /&gt;
			fromObjectType: &amp;quot;userContact&amp;quot; , // objectType at fromServiceNameTag&lt;br /&gt;
		},&lt;br /&gt;
		email: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;userContactId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; userContactId&lt;br /&gt;
: a UUID generated when creating the email record, or sent by [[Service - User Contact Manager]]&lt;br /&gt;
; email&lt;br /&gt;
: string&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
* [[Service - User Contact Manager]] sets the notification group's uniqueID to be {emailUniqueId}_{uuid}, we could split the notificationGroupUniqueId to pull out the emailUniqueId, alternative is we also add the emailID to notification group's additionalData which is what we will do because it is more strongly defined&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Contact Method Email|Contact Method Email]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Contact Method Email]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=2944</id>
		<title>Service - User Contact Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=2944"/>
		<updated>2025-03-10T02:44:24Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Manages contact methods available to users. Records each users contacts.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-core-shared/izara-core-shared-user-contact-manager&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== userContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;userContact&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				        // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			        // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;userContactSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
					fieldName: &amp;quot;contactTag&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		userContactId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		methodTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			name: &amp;quot;userContactId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; userContactId&lt;br /&gt;
: comes from random UUID&lt;br /&gt;
; methodTag&lt;br /&gt;
: comes from {serviceName} of receiver service &lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== hasUserContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasUserContact&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserAccount&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledUserContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledUserContact&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserAccount&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;user&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Creating a new user contact =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of available contact methods&lt;br /&gt;
* User choses one and and is directed to that contact method handler to create the actual entry&lt;br /&gt;
* Once the user contact is created in the handler service a message gets sent to this service which creates it's own UserContacts record&lt;br /&gt;
&lt;br /&gt;
= Creating a new notification group =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of the users contacts&lt;br /&gt;
* How do we handle authentication, notification manager has no record of the userId eg for notificationGroups, only UCM has this&lt;br /&gt;
* (for now not direct to notification manager, any updates go through UCM, but if can figure out some token system could go direct to notification manager) User choses one and is directed to [[Service - Notification Manager|Notification Manager]] service&lt;br /&gt;
* For the endpoint given to Notification Manager try having notifications go directly to the contact method handler. Another option would be to pass back through this service but I cannot see any benefit in this.&lt;br /&gt;
* Notification Manager completes the flow, creating a notification group and associated notification records&lt;br /&gt;
&lt;br /&gt;
= Location specific contact methods =&lt;br /&gt;
&lt;br /&gt;
* Could add ways of restricting the available contact methods to ones the user is likely to want&lt;br /&gt;
* eg: by location (eg country)&lt;br /&gt;
* but have a way that the user can choose any method&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
* If method handler changes the contactName, a message will be sent to User Contact Manager to update its record, contactName is part of the sort key, not sure if it can be updated, if not need to copy the record to a new record with the updated contactName&lt;br /&gt;
* If method handler disables a contact, disable here and send message to [[Service - Notification Manager|Notification Manager]] service which will disable notifications and tell [[Service - Activity Switchboard|Activity Switchboard]] service to remove all associated triggers&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - User Contact Manager|User Contact Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| User Contact Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2909</id>
		<title>Service - Activity Switchboard</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2909"/>
		<updated>2025-03-07T03:42:57Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Receives most messages sent within the entire project's serverless flow, allows services to register a set of triggers that are checked when each message is received, if all triggers pass a message is sent out for subscribed services.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/activity-switchboard/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== TriggerGroups ==&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: comes from: {receiverTag}_{uniqueId}&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service name eg. NotificationMgr&lt;br /&gt;
; uniqueId &lt;br /&gt;
: comes from receiver service, eg: {notificationId}, cannot include underscores&lt;br /&gt;
; triggers&lt;br /&gt;
: array of objects (DynamoDB list?)&lt;br /&gt;
: each element has triggerId, triggerGroupId, valueType, propertyName, propertyValueString or propertyValueNumber&lt;br /&gt;
&lt;br /&gt;
== Triggers ==&lt;br /&gt;
&lt;br /&gt;
; triggerId (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: string&lt;br /&gt;
: create by [[#triggerGroup]]&lt;br /&gt;
; valueType&lt;br /&gt;
: string &lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== triggerGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggers, all triggers for a trigger group must pass for the trigger group to pass&lt;br /&gt;
* no sort key&lt;br /&gt;
* if multiple values are sent for a trigger we consider this an &amp;quot;or&amp;quot; check, any of the values can match. We do this by saving each value as a separate trigger, then when checking a trigger groups child triggers we have a counter that enables a check if any one of the values matches&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;triggerGroup&amp;quot;,&lt;br /&gt;
	canDelete: true,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;, &amp;quot;get&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;TriggerGroups&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggers: {&lt;br /&gt;
			type: &amp;quot;array&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		receiverTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		uniqueId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldNames: [&amp;quot;receiverTag&amp;quot;, &amp;quot;uniqueId&amp;quot;],&lt;br /&gt;
			name: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: comes from: {receiverTag}_{uniqueId}&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service name eg. NotificationMgr&lt;br /&gt;
; uniqueId &lt;br /&gt;
: comes from receiver service, eg: {notificationId}, cannot include underscores&lt;br /&gt;
; triggers&lt;br /&gt;
: array of objects (DynamoDB list?)&lt;br /&gt;
: each element has triggerId, triggerGroupId, valueType, propertyName,  propertyValueString and propertyValueNumber&lt;br /&gt;
&lt;br /&gt;
=== trigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;trigger&amp;quot;,&lt;br /&gt;
	canDelete: true,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;, &amp;quot;get&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;Triggers&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;ActivitySwitchboard&amp;quot;,&lt;br /&gt;
			fromObjectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
: attributes is for message attributes&lt;br /&gt;
: property is from the data sent inside the message body&lt;br /&gt;
: hash property name and value so no ambiguity about underscores/spaces etc..&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: (sort key)&lt;br /&gt;
: created by [[#triggerGroup]]&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Efficiency =&lt;br /&gt;
&lt;br /&gt;
* Service will result in a large number of queries to Triggers table, every message will need to make a query for every field set as an activityTrigger&lt;br /&gt;
* Try to make this as efficient as possible&lt;br /&gt;
* To reduce number of queries made to Tiggers table we use the msgCfg for any message received&lt;br /&gt;
** MsgCfg sets which properties can be used as triggers, others are not queried&lt;br /&gt;
&lt;br /&gt;
= Handling msgCfgs =&lt;br /&gt;
&lt;br /&gt;
* msgCfgs get updated from [[Service - Message Config Manager|Message Config Manager]] service, we do this by subscribing to Message Config Manager's OutMsgCfgUpdate topic&lt;br /&gt;
* Only want to process messages from certain In/Out topics, it will be most topics but does not have to be all&lt;br /&gt;
* Set which topics to subscribe to by setting activitySwitchboard = true in msgCfg&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
* If more efficient can use cache for regular DynamoDB queries&lt;br /&gt;
* Could add other matching methods such as greater than or less than, in DynamoDB this might be more efficient to add as separate table with its own structure, eg: partition key is the field reference, sort key is the amount, then use sort key to return matching triggers. Danger of bad partitioning in DynamoDB (or hitting limits) due to large numbers of sort keys associated with one partition key&lt;br /&gt;
* could consider how to incorporate includes or checking within a set of options set in the trigger, again might need specialized table structure&lt;br /&gt;
* Might be able to optimise by using SNS &amp;gt; stream instead of SNS &amp;gt; SQS for all incomming messages&lt;br /&gt;
* One system level receiving service could be specialized logs, eg logs per service per user/per product/etc&lt;br /&gt;
* Topic name is not fixed part of the TriggersGroup structure, allow for trigger groups that are not connected to specific topics, but can pass messages from any topic that matches other triggers&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Activity Switchboard|Working_documents - Activity Switchboard]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Activity Switchboard]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2907</id>
		<title>Service - Notification Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2907"/>
		<updated>2025-03-06T08:55:12Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/notification-manager/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== PendingConsolidation Table ==&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: type: string&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: type: number&lt;br /&gt;
: {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
; activityMsg&lt;br /&gt;
: type: object&lt;br /&gt;
: object containing ''messageAttributes'' and ''message''&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== notificationGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many notifications together&lt;br /&gt;
* Simplifies management of similar notifications&lt;br /&gt;
* Allows for consolidation of notifications at notification group level&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationGroup&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationGroupSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationGroupName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{ &lt;br /&gt;
					fieldName: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedType&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedFrequency&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedNextSendDue&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;number&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedSendIfEmpty&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; notificationGroupName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; consolidated&lt;br /&gt;
: true|false&lt;br /&gt;
; consolidatedType&lt;br /&gt;
: overview|detailed&lt;br /&gt;
: ''overview'' sends a count of each type of notification, ''detailed'' lists each activity message&lt;br /&gt;
; consolidatedFrequency&lt;br /&gt;
: not sure, maybe use some sort of standard like cron&lt;br /&gt;
; consolidatedNextSendDue&lt;br /&gt;
: timestamp for next due time to send notification&lt;br /&gt;
; consolidatedSendIfEmpty&lt;br /&gt;
: true|false&lt;br /&gt;
&lt;br /&gt;
=== notification ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notification&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
   // overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; notificationName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
&lt;br /&gt;
=== notificationTrigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationTrigger&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;, &amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationTriggerId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		valueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		values: {&lt;br /&gt;
			type: &amp;quot;arrayMixed&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationTriggerId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationTriggerId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; propertyName&lt;br /&gt;
: can be a nested property, use dot notation&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; valueString&lt;br /&gt;
: type: string&lt;br /&gt;
; valueNumber&lt;br /&gt;
: type: number&lt;br /&gt;
; values&lt;br /&gt;
: type: arrayMixed&lt;br /&gt;
&lt;br /&gt;
=== consolidated ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [],      // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 		 // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 	 // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;PendingConsolidation&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;NotificationManager&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		time: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		activityMsg: {&lt;br /&gt;
			type: &amp;quot;object&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;time&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId&lt;br /&gt;
; activityMsg&lt;br /&gt;
: copy of the message delivered from Activity Switchboard&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== hasNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: links userContact to notificationGroup&lt;br /&gt;
&lt;br /&gt;
=== disabledNotificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledNotificationGroup&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== hasNotification ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasNotification&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== disabledNotification ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledNotification&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== hasNotificationTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasNotificationTrigger&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationTrigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,	&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== disabledNotificationTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledNotificationTrigger&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationTrigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= value or values array =&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName &lt;br /&gt;
&lt;br /&gt;
= serviceName and topicName triggers =&lt;br /&gt;
&lt;br /&gt;
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass&lt;br /&gt;
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes&lt;br /&gt;
* Can set serviceName+topicName, then only messages in that topic will pass&lt;br /&gt;
* serviceName and topicName can both be submitted as &amp;quot;values&amp;quot; array, creating multiple trigger groups for each combination&lt;br /&gt;
&lt;br /&gt;
= Consolidated notifications =&lt;br /&gt;
&lt;br /&gt;
== How to trigger processing for time based consolidations == &lt;br /&gt;
&lt;br /&gt;
* ....&lt;br /&gt;
* 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)&lt;br /&gt;
&lt;br /&gt;
= Formating the notification body = &lt;br /&gt;
&lt;br /&gt;
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body&lt;br /&gt;
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs&lt;br /&gt;
&lt;br /&gt;
= Ideas = &lt;br /&gt;
&lt;br /&gt;
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level&lt;br /&gt;
* 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&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Notification Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Contact_Method_Email&amp;diff=2905</id>
		<title>Service - Contact Method Email</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Contact_Method_Email&amp;diff=2905"/>
		<updated>2025-03-06T07:02:06Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Manages email contact methods for users, details are not part of the public transparency followed across the majority of the project in order to protect against emails being scraped.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/contact-method-email/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== EmailAddress==&lt;br /&gt;
&lt;br /&gt;
=== Fields ===&lt;br /&gt;
&lt;br /&gt;
; userContactId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: a UUID generated when creating the email record, or sent by [[Service - User Contact Manager]]&lt;br /&gt;
; email&lt;br /&gt;
: string &lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== email ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;email&amp;quot;,&lt;br /&gt;
    extendObjType: { // core object that this object extends&lt;br /&gt;
      objectType: &amp;quot;userContact&amp;quot;,&lt;br /&gt;
      serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
    },&lt;br /&gt;
    storageResources: {},&lt;br /&gt;
    fieldNames: {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== emailAddress ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;emailAddress&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [],         // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 	        // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 		// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;, &amp;quot;get&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;EmailAddress&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		userContactId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		email: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;userContactId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; userContactId&lt;br /&gt;
: a UUID generated when creating the email record, or sent by [[Service - User Contact Manager]]&lt;br /&gt;
; email&lt;br /&gt;
: string&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
* [[Service - User Contact Manager]] sets the notification group's uniqueID to be {emailUniqueId}_{uuid}, we could split the notificationGroupUniqueId to pull out the emailUniqueId, alternative is we also add the emailID to notification group's additionalData which is what we will do because it is more strongly defined&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Contact Method Email|Contact Method Email]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Contact Method Email]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2892</id>
		<title>2025-02-20 - Integration Test examples</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2892"/>
		<updated>2025-02-24T08:58:39Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Integration Testing]]&lt;br /&gt;
&lt;br /&gt;
[[Service - Integration Test Config]]&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
:The example of propeerties in resources.json, events.json and tests.json for upload to S3 bucket &lt;br /&gt;
:[[Service - Integration Test Config|Integration Test Config]] for config of each property in these files&lt;br /&gt;
= resources.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
	&amp;quot;Lambda&amp;quot;:{&lt;br /&gt;
		&amp;quot;{FunctionNameHdrSqs}&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/FunctionNameHdrSqs.js&amp;quot;,&lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;FunctionNameHdrSqs&amp;quot;	// FunctionName is full name of function after deploy&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ProcessComplexFilterHdrSqs&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/ProcessComplexFilterHdrSqs.js&amp;quot;, &lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;ProcessComplexFilterHdrSqs&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;Dynamodb&amp;quot;: {&lt;br /&gt;
		&amp;quot;filterMain&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;filterMain&amp;quot; &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;tableNameBB&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;tableNameBB&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= events.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// Layout of file events.json &lt;br /&gt;
{ &lt;br /&gt;
	input:{}, &lt;br /&gt;
	output:{}, &lt;br /&gt;
	dynamodb:{}&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
// An example of naming an eventTag for each event&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},					            // for input &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},				        // for output&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;: {}  // for dynamodb&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot; is called &amp;lt;u&amp;gt;inputEventTag&amp;lt;/u&amp;gt;&lt;br /&gt;
* for &amp;lt;u&amp;gt;outputEventTag&amp;lt;/u&amp;gt; is '''Output_{inputEvenTag}'''&lt;br /&gt;
:: eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
* for dynamodb is '''{inputEventTag}__dynamodb_{action}_{tablename}'''&lt;br /&gt;
:: eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
: &amp;lt;u&amp;gt;'''{action}''' is only ''put'' or ''update'', and in case of ''update'' must only test the last step of update &amp;lt;/u&amp;gt;&lt;br /&gt;
== Sns/Sqs, Dsq and Invoke ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of Sns/Sqs, Dsq and Invoke&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
				&amp;quot;testValueMatches&amp;quot;: false,  // no child properties will be tested &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
					} &lt;br /&gt;
				} &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
				&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
					&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
				] &lt;br /&gt;
			} &lt;br /&gt;
		}, &lt;br /&gt;
		&lt;br /&gt;
		// settings for Sns/Sqs&lt;br /&gt;
		&amp;quot;snsSqsTrigger&amp;quot;: true,        // set when event is initial stage of Sns&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: {        // optional &lt;br /&gt;
			&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
			&amp;quot;properties&amp;quot;: { &lt;br /&gt;
				&amp;quot;msgTag&amp;quot;: { 	      // require &lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;msgTag&amp;quot; &lt;br /&gt;
				}, &lt;br /&gt;
				&amp;quot;propertyD&amp;quot;: { &lt;br /&gt;
					&amp;quot;forStageMatching&amp;quot;: true,     // default: false, whether this property is used to match stage config during integration tests&lt;br /&gt;
					&amp;quot;testValueMatches&amp;quot;: false,    // default: true, whether value of this property is checked when performing test&lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;valuePropertyD&amp;quot; &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
		&lt;br /&gt;
		// settings for Dsq&lt;br /&gt;
		// DirectSqs set in HdrDsq if have messageAttributeValidatorSchema and required property in messageAttributes eg: messageTag &lt;br /&gt;
		&amp;quot;DirectSqs&amp;quot;: true, 			// set when event is initial stage of Dsq&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: { 	    // optional  &lt;br /&gt;
			&amp;quot;messageTag&amp;quot;: { &lt;br /&gt;
				&amp;quot;DataType&amp;quot;: &amp;quot;String&amp;quot;, &lt;br /&gt;
				&amp;quot;StringValue&amp;quot;: &amp;quot;messageAttributes_is&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}&lt;br /&gt;
			...&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== API ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of API&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;forstageMatvhing&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
						&amp;quot;properties&amp;quot;: { &lt;br /&gt;
							&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
								&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
							} &lt;br /&gt;
						} &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
						&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
							&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
						] &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}&lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;&lt;br /&gt;
					}, &lt;br /&gt;
					...&lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}  &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Dynamodb ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of Dynamodb &lt;br /&gt;
{&lt;br /&gt;
	{inputEventTag}__dynamodb_{action}_{tablename}: { // eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			(property is update)... &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;keyValues&amp;quot;: { &lt;br /&gt;
			{partitionKey}: &amp;quot;4865d2451a621cb7f71225cfabf2df4f98e20801 &amp;quot;, &lt;br /&gt;
			{sortKey}: &amp;quot;cdb170690571df7522b3850920fce02507c077ee&amp;quot; &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== NoRetryError ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of NoRetryError output &lt;br /&gt;
{&lt;br /&gt;
	Output_{inputEvenTag}: { // eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;errorMessage&amp;quot;: { &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;Not found schema of {serviceTag:TestGraphHandler, objectType:notFoundObjType}&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= tests.json = &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example test &lt;br /&gt;
[ &lt;br /&gt;
	{ &lt;br /&gt;
		&amp;quot;integrationTestTag&amp;quot;: &amp;quot;test_S3_ByDev&amp;quot;, &lt;br /&gt;
		&amp;quot;productionSafe&amp;quot;: false,&lt;br /&gt;
		&amp;quot;errorIfStageUndefined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;errorIfInvokeUndefined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;stages&amp;quot;: [&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;initialStage&amp;quot;: true, &lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessComplexFilterHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;snsServiceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;,     // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke &lt;br /&gt;
				&amp;quot;snsTopic&amp;quot;: &amp;quot;InProcessComplexFilter&amp;quot;,  // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke&lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;Output_Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;BatchSqsMessages&amp;quot;: true&lt;br /&gt;
				&lt;br /&gt;
				// Settings if have test dynamodb&lt;br /&gt;
				&amp;quot;dynamodbOutputEventIdentifiers&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
						&amp;quot;resourceName&amp;quot;: &amp;quot;FilterMain&amp;quot;, &lt;br /&gt;
						&amp;quot;eventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic_dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				],&lt;br /&gt;
				&lt;br /&gt;
				&lt;br /&gt;
				// Settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;invokes&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				], 	 &lt;br /&gt;
			},&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; // require &amp;quot;eventStageTag&amp;quot; if have settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;SellOfferMgr_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;,&lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;SellOfferMgr&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessLogicalHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;output_intTesting_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;, &lt;br /&gt;
			},&lt;br /&gt;
			... &lt;br /&gt;
		] &lt;br /&gt;
	},&lt;br /&gt;
	... &lt;br /&gt;
] &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2025-02-20]]&lt;br /&gt;
[[Category:Working documents - Developer guide for making Integration Tests| 2025-02-20]]&lt;br /&gt;
[[Category:Working documents - Integration Test Config| 2025-02-20]]&lt;br /&gt;
[[Category:Working documents - Integration Testing| 2025-02-20]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2891</id>
		<title>2025-02-20 - Integration Test examples</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2891"/>
		<updated>2025-02-24T08:52:23Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
:The example of propeerties in resources.json, events.json and tests.json for upload to S3 bucket &lt;br /&gt;
:[[Service - Integration Test Config|Integration Test Config]] for config of each property in these files&lt;br /&gt;
= resources.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
	&amp;quot;Lambda&amp;quot;:{&lt;br /&gt;
		&amp;quot;{FunctionNameHdrSqs}&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/FunctionNameHdrSqs.js&amp;quot;,&lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;FunctionNameHdrSqs&amp;quot;	// FunctionName is full name of function after deploy&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ProcessComplexFilterHdrSqs&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/ProcessComplexFilterHdrSqs.js&amp;quot;, &lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;ProcessComplexFilterHdrSqs&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;Dynamodb&amp;quot;: {&lt;br /&gt;
		&amp;quot;filterMain&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;filterMain&amp;quot; &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;tableNameBB&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;tableNameBB&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= events.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// Layout of file events.json &lt;br /&gt;
{ &lt;br /&gt;
	input:{}, &lt;br /&gt;
	output:{}, &lt;br /&gt;
	dynamodb:{}&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
// An example of naming an eventTag for each event&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},					            // for input &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},				        // for output&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;: {}  // for dynamodb&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot; is called &amp;lt;u&amp;gt;inputEventTag&amp;lt;/u&amp;gt;&lt;br /&gt;
* for &amp;lt;u&amp;gt;outputEventTag&amp;lt;/u&amp;gt; is '''Output_{inputEvenTag}'''&lt;br /&gt;
:: eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
* for dynamodb is '''{inputEventTag}__dynamodb_{action}_{tablename}'''&lt;br /&gt;
:: eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
: &amp;lt;u&amp;gt;'''{action}''' is only ''put'' or ''update'', and in case of ''update'' must only test the last step of update &amp;lt;/u&amp;gt;&lt;br /&gt;
== Sns/Sqs, Dsq and Invoke ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of Sns/Sqs, Dsq and Invoke&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
				&amp;quot;testValueMatches&amp;quot;: false,  // no child properties will be tested &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
					} &lt;br /&gt;
				} &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
				&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
					&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
				] &lt;br /&gt;
			} &lt;br /&gt;
		}, &lt;br /&gt;
		&lt;br /&gt;
		// settings for Sns/Sqs&lt;br /&gt;
		&amp;quot;snsSqsTrigger&amp;quot;: true,        // set when event is initial stage of Sns&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: {        // optional &lt;br /&gt;
			&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
			&amp;quot;properties&amp;quot;: { &lt;br /&gt;
				&amp;quot;msgTag&amp;quot;: { 	      // require &lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;msgTag&amp;quot; &lt;br /&gt;
				}, &lt;br /&gt;
				&amp;quot;propertyD&amp;quot;: { &lt;br /&gt;
					&amp;quot;forStageMatching&amp;quot;: true,     // default: false, whether this property is used to match stage config during integration tests&lt;br /&gt;
					&amp;quot;testValueMatches&amp;quot;: false,    // default: true, whether value of this property is checked when performing test&lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;valuePropertyD&amp;quot; &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
		&lt;br /&gt;
		// settings for Dsq&lt;br /&gt;
		// DirectSqs set in HdrDsq if have messageAttributeValidatorSchema and required property in messageAttributes eg: messageTag &lt;br /&gt;
		&amp;quot;DirectSqs&amp;quot;: true, 			// set when event is initial stage of Dsq&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: { 	    // optional  &lt;br /&gt;
			&amp;quot;messageTag&amp;quot;: { &lt;br /&gt;
				&amp;quot;DataType&amp;quot;: &amp;quot;String&amp;quot;, &lt;br /&gt;
				&amp;quot;StringValue&amp;quot;: &amp;quot;messageAttributes_is&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}&lt;br /&gt;
			...&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== API ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of API&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;forstageMatvhing&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
						&amp;quot;properties&amp;quot;: { &lt;br /&gt;
							&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
								&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
							} &lt;br /&gt;
						} &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
						&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
							&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
						] &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}&lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;&lt;br /&gt;
					}, &lt;br /&gt;
					...&lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}  &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Dynamodb ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of Dynamodb &lt;br /&gt;
{&lt;br /&gt;
	{inputEventTag}__dynamodb_{action}_{tablename}: { // eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			(property is update)... &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;keyValues&amp;quot;: { &lt;br /&gt;
			{partitionKey}: &amp;quot;4865d2451a621cb7f71225cfabf2df4f98e20801 &amp;quot;, &lt;br /&gt;
			{sortKey}: &amp;quot;cdb170690571df7522b3850920fce02507c077ee&amp;quot; &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== NoRetryError ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of NoRetryError output &lt;br /&gt;
{&lt;br /&gt;
	Output_{inputEvenTag}: { // eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;errorMessage&amp;quot;: { &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;Not found schema of {serviceTag:TestGraphHandler, objectType:notFoundObjType}&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= tests.json = &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example test &lt;br /&gt;
[ &lt;br /&gt;
	{ &lt;br /&gt;
		&amp;quot;integrationTestTag&amp;quot;: &amp;quot;test_S3_ByDev&amp;quot;, &lt;br /&gt;
		&amp;quot;productionSafe&amp;quot;: false,&lt;br /&gt;
		&amp;quot;errorIfStageUndefined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;errorIfInvokeUndefined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;stages&amp;quot;: [&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;initialStage&amp;quot;: true, &lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessComplexFilterHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;snsServiceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;,     // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke &lt;br /&gt;
				&amp;quot;snsTopic&amp;quot;: &amp;quot;InProcessComplexFilter&amp;quot;,  // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke&lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;Output_Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;BatchSqsMessages&amp;quot;: true&lt;br /&gt;
				&lt;br /&gt;
				// Settings if have test dynamodb&lt;br /&gt;
				&amp;quot;dynamodbOutputEventIdentifiers&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
						&amp;quot;resourceName&amp;quot;: &amp;quot;FilterMain&amp;quot;, &lt;br /&gt;
						&amp;quot;eventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic_dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				],&lt;br /&gt;
				&lt;br /&gt;
				&lt;br /&gt;
				// Settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;invokes&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				], 	 &lt;br /&gt;
			},&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; // require &amp;quot;eventStageTag&amp;quot; if have settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;SellOfferMgr_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;,&lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;SellOfferMgr&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessLogicalHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;output_intTesting_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;, &lt;br /&gt;
			},&lt;br /&gt;
			... &lt;br /&gt;
		] &lt;br /&gt;
	},&lt;br /&gt;
	... &lt;br /&gt;
] &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2025-02-20]]&lt;br /&gt;
[[Category:Working documents - Developer guide for making Integration Tests| 2025-02-20]]&lt;br /&gt;
[[Category:Working documents - Integration Test Config| 2025-02-20]]&lt;br /&gt;
[[Category:Working documents - Integration Testing| 2025-02-20]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Developer_guide_for_making_Integration_Tests&amp;diff=2890</id>
		<title>Developer guide for making Integration Tests</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Developer_guide_for_making_Integration_Tests&amp;diff=2890"/>
		<updated>2025-02-24T08:51:32Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
:How to create folder and file in S3 bucket for prepare integrationTest&lt;br /&gt;
:[[Service - Integration Test Config|Integration Test Config]] for config of property in file resources.json, events.json and tests.json&lt;br /&gt;
= Setup integrationtest =&lt;br /&gt;
== Create folder ==&lt;br /&gt;
: Create your folder in bucket '''integrationtest-config-us-east-2/test_config/{&amp;lt;u&amp;gt;your folder name&amp;lt;/u&amp;gt;}''' in S3 bucket&lt;br /&gt;
:* '''{&amp;lt;u&amp;gt;your folder name&amp;lt;/u&amp;gt;}''' maybe is your nickname eg. jamesDev, nicoleDev&lt;br /&gt;
:* Can create a subfolder in your folder for each test case or service that you want to test&lt;br /&gt;
:: eg. - integrationtest-config-us-east-2/test_config/jamesDev/SellOfferManager&lt;br /&gt;
::: - integrationtest-config-us-east-2/test_config/jamesDev/ProcessLogical&lt;br /&gt;
::: - integrationtest-config-us-east-2/test_config/jamesDev/SellOfferManager/ProcessLogical&lt;br /&gt;
== Create file ==&lt;br /&gt;
# Create a file in a folder that is used for testing&lt;br /&gt;
#* ''resources.json / events.json / tests.json''&lt;br /&gt;
#* '''resources.json''' and '''events.json''' is json object&lt;br /&gt;
#* '''tests.json''' is json array of objects&lt;br /&gt;
#* Can create these files in a SharedResource folder and share it with another test for collaborative usage&lt;br /&gt;
#:: eg. integrationtest-config-us-east-2/test_config/jamesDev/SellOfferManager/SharedResource/resources.json&lt;br /&gt;
#* In the main prefix for testing, you don’t need to upload every file. If you have a file in SharedResource, you can skip uploading it &lt;br /&gt;
## '''resources.json'''&lt;br /&gt;
##* Syntax; [[Service_-_Integration_Test_Config#resources.js Syntax|resources Syntax]]&lt;br /&gt;
## '''events.json'''&lt;br /&gt;
##* Syntax; [[Service_-_Integration_Test_Config#events/*.js_Syntax|events Syntax]]&lt;br /&gt;
##* Not use &amp;lt;code&amp;gt;&amp;lt;s&amp;gt;isObject&amp;lt;/s&amp;gt;&amp;lt;/code&amp;gt;. Use &amp;lt;code&amp;gt;properties&amp;lt;/code&amp;gt; instead, if is an object will look for child properties&lt;br /&gt;
##* Not use &amp;lt;code&amp;gt;&amp;lt;s&amp;gt;isStringSet&amp;lt;/s&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
##* If have many stage, Set &amp;lt;code&amp;gt;initialStage&amp;lt;/code&amp;gt; is true only initial stage. Next stage not set this setting&lt;br /&gt;
##* If &amp;lt;code&amp;gt;snsSqsTrigger&amp;lt;/code&amp;gt; is true but not set snsServiceName or snsTopic --&amp;gt; direct invoke &lt;br /&gt;
##* If &amp;lt;code&amp;gt;DirectSqs&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;BatchSqsMessages&amp;lt;/code&amp;gt; is true --&amp;gt; direct invoke&lt;br /&gt;
##* If &amp;lt;code&amp;gt;snsSqsTrigger&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;DirectSqs&amp;lt;/code&amp;gt; is false or not set --&amp;gt; direct invoke&lt;br /&gt;
##* &amp;lt;code&amp;gt;forStageMatching&amp;lt;/code&amp;gt; &amp;lt;u&amp;gt;Use in test important parameter&amp;lt;/u&amp;gt;&lt;br /&gt;
##* &amp;lt;code&amp;gt;testValueMatches&amp;lt;/code&amp;gt; &amp;lt;u&amp;gt;For random parameter eg. timeStamp or uuid&amp;lt;/u&amp;gt;&lt;br /&gt;
##* If value in array or number or string is &amp;lt;u&amp;gt;random&amp;lt;/u&amp;gt;. '''Use testValueMatches: true not set forStageMatching: true'''  &lt;br /&gt;
##* Multiple eventTags can be added to a single file&lt;br /&gt;
##* Have 6 types of events; Dynamodb, Sns/Sqs, Dsq, Invoke, API and NoRetryError &lt;br /&gt;
## '''tests.json'''&lt;br /&gt;
##* Syntax; [[Service_-_Integration_Test_Config#tests/*.js_Syntax|tests Syntax]]&lt;br /&gt;
##* Not use &amp;lt;code&amp;gt;&amp;lt;s&amp;gt;dynamoDbOutput&amp;lt;/s&amp;gt;&amp;lt;/code&amp;gt;. Use &amp;lt;code&amp;gt;dynamodbOutputEventIdentifiers&amp;lt;/code&amp;gt; instead&lt;br /&gt;
# Upload file to S3 bucket by batchscript or upload to S3 bucket directly&lt;br /&gt;
#:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash &lt;br /&gt;
set -u &lt;br /&gt;
&lt;br /&gt;
localPath=&amp;quot;/home/dogsrcute/Programming mint/Fixed_Integrationtest/PluemDev/paginate/2stages&amp;quot; &lt;br /&gt;
bucketName=s3://integrationtest-config-us-east-2/test_config/mintDev/fixed/PluemDev/2stages/ &lt;br /&gt;
&lt;br /&gt;
aws s3 cp &amp;quot;$localPath&amp;quot; &amp;quot;$bucketName&amp;quot; --recursive &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#* localPath is part to files for upload &lt;br /&gt;
#* bucketName is your bucket&lt;br /&gt;
== Type of parameter ==&lt;br /&gt;
: Parameter type for create property in events.json&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
object: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	properties: {} &lt;br /&gt;
}, &lt;br /&gt;
array: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	eventValue: [] &lt;br /&gt;
}, &lt;br /&gt;
number: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	value: 1111 &lt;br /&gt;
}, &lt;br /&gt;
string: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	value: &amp;quot;hi&amp;quot; &lt;br /&gt;
}, &lt;br /&gt;
boolean: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	value: true &lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
// Case empty &lt;br /&gt;
EmptyString:{ &lt;br /&gt;
	value: &amp;quot;&amp;quot; &lt;br /&gt;
}, &lt;br /&gt;
EmptyArray:{ &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	eventValue: [] &lt;br /&gt;
}, &lt;br /&gt;
EmptyObject:{ &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	value: {} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= Test =&lt;br /&gt;
# create params for invoke &amp;lt;u&amp;gt;IntTestingTestInitiateIntegrationTestHdrInv&amp;lt;/u&amp;gt; function&lt;br /&gt;
#:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
	&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;,  &lt;br /&gt;
	&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;,  &lt;br /&gt;
	&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessComplexFilterHdrSqs&amp;quot;,  &lt;br /&gt;
	&amp;quot;integrationTestTags&amp;quot;: [ &lt;br /&gt;
		&amp;quot;test_S3_ByDev&amp;quot;  &lt;br /&gt;
	],  &lt;br /&gt;
	&amp;quot;bucketName&amp;quot;: &amp;quot;integrationtest-config-us-east-2&amp;quot;,  &lt;br /&gt;
	&amp;quot;bucketPrefix&amp;quot;:[  &lt;br /&gt;
		&amp;quot;test_config/mintDev/basicCase/&amp;quot;, &lt;br /&gt;
		&amp;quot;test_config/mintDev/SharedResource/&amp;quot;  &lt;br /&gt;
	]  &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Find results in IntTestingTestTestRecord table&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Developer guide for making Integration Tests|Working_documents - Developer guide for making Integration Tests]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Integration_Test_Examples&amp;diff=2889</id>
		<title>Integration Test Examples</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Integration_Test_Examples&amp;diff=2889"/>
		<updated>2025-02-24T08:49:35Z</updated>

		<summary type="html">&lt;p&gt;Pong: Pong moved page Integration Test Examples to 2025-02-20 - Integration Test examples&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[2025-02-20 - Integration Test examples]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2888</id>
		<title>2025-02-20 - Integration Test examples</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2888"/>
		<updated>2025-02-24T08:49:34Z</updated>

		<summary type="html">&lt;p&gt;Pong: Pong moved page Integration Test Examples to 2025-02-20 - Integration Test examples&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
:The example of propeerties in resources.json, events.json and tests.json for upload to S3 bucket &lt;br /&gt;
:[[Service - Integration Test Config|Integration Test Config]] for config of each property in these files&lt;br /&gt;
= resources.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
	&amp;quot;Lambda&amp;quot;:{&lt;br /&gt;
		&amp;quot;{FunctionNameHdrSqs}&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/FunctionNameHdrSqs.js&amp;quot;,&lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;FunctionNameHdrSqs&amp;quot;	// FunctionName is full name of function after deploy&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ProcessComplexFilterHdrSqs&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/ProcessComplexFilterHdrSqs.js&amp;quot;, &lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;ProcessComplexFilterHdrSqs&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;Dynamodb&amp;quot;: {&lt;br /&gt;
		&amp;quot;filterMain&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;filterMain&amp;quot; &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;tableNameBB&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;tableNameBB&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= events.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// Layout of file events.json &lt;br /&gt;
{ &lt;br /&gt;
	input:{}, &lt;br /&gt;
	output:{}, &lt;br /&gt;
	dynamodb:{}&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
// An example of naming an eventTag for each event&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},					            // for input &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},				        // for output&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;: {}  // for dynamodb&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot; is called &amp;lt;u&amp;gt;inputEventTag&amp;lt;/u&amp;gt;&lt;br /&gt;
* for &amp;lt;u&amp;gt;outputEventTag&amp;lt;/u&amp;gt; is '''Output_{inputEvenTag}'''&lt;br /&gt;
:: eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
* for dynamodb is '''{inputEventTag}__dynamodb_{action}_{tablename}'''&lt;br /&gt;
:: eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
: &amp;lt;u&amp;gt;'''{action}''' is only ''put'' or ''update'', and in case of ''update'' must only test the last step of update &amp;lt;/u&amp;gt;&lt;br /&gt;
== Sns/Sqs, Dsq and Invoke ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of Sns/Sqs, Dsq and Invoke&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
				&amp;quot;testValueMatches&amp;quot;: false,  // no child properties will be tested &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
					} &lt;br /&gt;
				} &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
				&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
					&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
				] &lt;br /&gt;
			} &lt;br /&gt;
		}, &lt;br /&gt;
		&lt;br /&gt;
		// settings for Sns/Sqs&lt;br /&gt;
		&amp;quot;snsSqsTrigger&amp;quot;: true,        // set when event is initial stage of Sns&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: {        // optional &lt;br /&gt;
			&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
			&amp;quot;properties&amp;quot;: { &lt;br /&gt;
				&amp;quot;msgTag&amp;quot;: { 	      // require &lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;msgTag&amp;quot; &lt;br /&gt;
				}, &lt;br /&gt;
				&amp;quot;propertyD&amp;quot;: { &lt;br /&gt;
					&amp;quot;forStageMatching&amp;quot;: true,     // default: false, whether this property is used to match stage config during integration tests&lt;br /&gt;
					&amp;quot;testValueMatches&amp;quot;: false,    // default: true, whether value of this property is checked when performing test&lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;valuePropertyD&amp;quot; &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
		&lt;br /&gt;
		// settings for Dsq&lt;br /&gt;
		// DirectSqs set in HdrDsq if have messageAttributeValidatorSchema and required property in messageAttributes eg: messageTag &lt;br /&gt;
		&amp;quot;DirectSqs&amp;quot;: true, 			// set when event is initial stage of Dsq&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: { 	    // optional  &lt;br /&gt;
			&amp;quot;messageTag&amp;quot;: { &lt;br /&gt;
				&amp;quot;DataType&amp;quot;: &amp;quot;String&amp;quot;, &lt;br /&gt;
				&amp;quot;StringValue&amp;quot;: &amp;quot;messageAttributes_is&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}&lt;br /&gt;
			...&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== API ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of API&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;forstageMatvhing&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
						&amp;quot;properties&amp;quot;: { &lt;br /&gt;
							&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
								&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
							} &lt;br /&gt;
						} &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
						&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
							&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
						] &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}&lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;&lt;br /&gt;
					}, &lt;br /&gt;
					...&lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}  &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Dynamodb ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of Dynamodb &lt;br /&gt;
{&lt;br /&gt;
	{inputEventTag}__dynamodb_{action}_{tablename}: { // eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			(property is update)... &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;keyValues&amp;quot;: { &lt;br /&gt;
			{partitionKey}: &amp;quot;4865d2451a621cb7f71225cfabf2df4f98e20801 &amp;quot;, &lt;br /&gt;
			{sortKey}: &amp;quot;cdb170690571df7522b3850920fce02507c077ee&amp;quot; &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== NoRetryError ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of NoRetryError output &lt;br /&gt;
{&lt;br /&gt;
	Output_{inputEvenTag}: { // eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;errorMessage&amp;quot;: { &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;Not found schema of {serviceTag:TestGraphHandler, objectType:notFoundObjType}&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= tests.json = &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example test &lt;br /&gt;
[ &lt;br /&gt;
	{ &lt;br /&gt;
		&amp;quot;integrationTestTag&amp;quot;: &amp;quot;test_S3_ByDev&amp;quot;, &lt;br /&gt;
		&amp;quot;productionSafe&amp;quot;: false,&lt;br /&gt;
		&amp;quot;errorIfStageUndefined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;errorIfInvokeUndefined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;stages&amp;quot;: [&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;initialStage&amp;quot;: true, &lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessComplexFilterHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;snsServiceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;,     // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke &lt;br /&gt;
				&amp;quot;snsTopic&amp;quot;: &amp;quot;InProcessComplexFilter&amp;quot;,  // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke&lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;Output_Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;BatchSqsMessages&amp;quot;: true&lt;br /&gt;
				&lt;br /&gt;
				// Settings if have test dynamodb&lt;br /&gt;
				&amp;quot;dynamodbOutputEventIdentifiers&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
						&amp;quot;resourceName&amp;quot;: &amp;quot;FilterMain&amp;quot;, &lt;br /&gt;
						&amp;quot;eventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic_dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				],&lt;br /&gt;
				&lt;br /&gt;
				&lt;br /&gt;
				// Settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;invokes&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				], 	 &lt;br /&gt;
			},&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; // require &amp;quot;eventStageTag&amp;quot; if have settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;SellOfferMgr_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;,&lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;SellOfferMgr&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessLogicalHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;output_intTesting_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;, &lt;br /&gt;
			},&lt;br /&gt;
			... &lt;br /&gt;
		] &lt;br /&gt;
	},&lt;br /&gt;
	... &lt;br /&gt;
] &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2887</id>
		<title>2025-02-20 - Integration Test examples</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2887"/>
		<updated>2025-02-24T08:48:04Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
:The example of propeerties in resources.json, events.json and tests.json for upload to S3 bucket &lt;br /&gt;
:[[Service - Integration Test Config|Integration Test Config]] for config of each property in these files&lt;br /&gt;
= resources.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
	&amp;quot;Lambda&amp;quot;:{&lt;br /&gt;
		&amp;quot;{FunctionNameHdrSqs}&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/FunctionNameHdrSqs.js&amp;quot;,&lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;FunctionNameHdrSqs&amp;quot;	// FunctionName is full name of function after deploy&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ProcessComplexFilterHdrSqs&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/ProcessComplexFilterHdrSqs.js&amp;quot;, &lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;ProcessComplexFilterHdrSqs&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;Dynamodb&amp;quot;: {&lt;br /&gt;
		&amp;quot;filterMain&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;filterMain&amp;quot; &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;tableNameBB&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;tableNameBB&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= events.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// Layout of file events.json &lt;br /&gt;
{ &lt;br /&gt;
	input:{}, &lt;br /&gt;
	output:{}, &lt;br /&gt;
	dynamodb:{}&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
// An example of naming an eventTag for each event&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},					            // for input &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},				        // for output&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;: {}  // for dynamodb&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot; is called &amp;lt;u&amp;gt;inputEventTag&amp;lt;/u&amp;gt;&lt;br /&gt;
* for &amp;lt;u&amp;gt;outputEventTag&amp;lt;/u&amp;gt; is '''Output_{inputEvenTag}'''&lt;br /&gt;
:: eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
* for dynamodb is '''{inputEventTag}__dynamodb_{action}_{tablename}'''&lt;br /&gt;
:: eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
: &amp;lt;u&amp;gt;'''{action}''' is only ''put'' or ''update'', and in case of ''update'' must only test the last step of update &amp;lt;/u&amp;gt;&lt;br /&gt;
== Sns/Sqs, Dsq and Invoke ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of Sns/Sqs, Dsq and Invoke&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
				&amp;quot;testValueMatches&amp;quot;: false,  // no child properties will be tested &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
					} &lt;br /&gt;
				} &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
				&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
					&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
				] &lt;br /&gt;
			} &lt;br /&gt;
		}, &lt;br /&gt;
		&lt;br /&gt;
		// settings for Sns/Sqs&lt;br /&gt;
		&amp;quot;snsSqsTrigger&amp;quot;: true,        // set when event is initial stage of Sns&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: {        // optional &lt;br /&gt;
			&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
			&amp;quot;properties&amp;quot;: { &lt;br /&gt;
				&amp;quot;msgTag&amp;quot;: { 	      // require &lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;msgTag&amp;quot; &lt;br /&gt;
				}, &lt;br /&gt;
				&amp;quot;propertyD&amp;quot;: { &lt;br /&gt;
					&amp;quot;forStageMatching&amp;quot;: true,     // default: false, whether this property is used to match stage config during integration tests&lt;br /&gt;
					&amp;quot;testValueMatches&amp;quot;: false,    // default: true, whether value of this property is checked when performing test&lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;valuePropertyD&amp;quot; &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
		&lt;br /&gt;
		// settings for Dsq&lt;br /&gt;
		// DirectSqs set in HdrDsq if have messageAttributeValidatorSchema and required property in messageAttributes eg: messageTag &lt;br /&gt;
		&amp;quot;DirectSqs&amp;quot;: true, 			// set when event is initial stage of Dsq&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: { 	    // optional  &lt;br /&gt;
			&amp;quot;messageTag&amp;quot;: { &lt;br /&gt;
				&amp;quot;DataType&amp;quot;: &amp;quot;String&amp;quot;, &lt;br /&gt;
				&amp;quot;StringValue&amp;quot;: &amp;quot;messageAttributes_is&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}&lt;br /&gt;
			...&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== API ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of API&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;forstageMatvhing&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
						&amp;quot;properties&amp;quot;: { &lt;br /&gt;
							&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
								&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
							} &lt;br /&gt;
						} &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
						&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
							&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
						] &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}&lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;&lt;br /&gt;
					}, &lt;br /&gt;
					...&lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}  &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Dynamodb ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of Dynamodb &lt;br /&gt;
{&lt;br /&gt;
	{inputEventTag}__dynamodb_{action}_{tablename}: { // eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			(property is update)... &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;keyValues&amp;quot;: { &lt;br /&gt;
			{partitionKey}: &amp;quot;4865d2451a621cb7f71225cfabf2df4f98e20801 &amp;quot;, &lt;br /&gt;
			{sortKey}: &amp;quot;cdb170690571df7522b3850920fce02507c077ee&amp;quot; &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== NoRetryError ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of NoRetryError output &lt;br /&gt;
{&lt;br /&gt;
	Output_{inputEvenTag}: { // eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;errorMessage&amp;quot;: { &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;Not found schema of {serviceTag:TestGraphHandler, objectType:notFoundObjType}&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= tests.json = &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example test &lt;br /&gt;
[ &lt;br /&gt;
	{ &lt;br /&gt;
		&amp;quot;integrationTestTag&amp;quot;: &amp;quot;test_S3_ByDev&amp;quot;, &lt;br /&gt;
		&amp;quot;productionSafe&amp;quot;: false,&lt;br /&gt;
		&amp;quot;errorIfStageUndefined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;errorIfInvokeUndefined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;stages&amp;quot;: [&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;initialStage&amp;quot;: true, &lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessComplexFilterHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;snsServiceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;,     // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke &lt;br /&gt;
				&amp;quot;snsTopic&amp;quot;: &amp;quot;InProcessComplexFilter&amp;quot;,  // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke&lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;Output_Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;BatchSqsMessages&amp;quot;: true&lt;br /&gt;
				&lt;br /&gt;
				// Settings if have test dynamodb&lt;br /&gt;
				&amp;quot;dynamodbOutputEventIdentifiers&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
						&amp;quot;resourceName&amp;quot;: &amp;quot;FilterMain&amp;quot;, &lt;br /&gt;
						&amp;quot;eventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic_dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				],&lt;br /&gt;
				&lt;br /&gt;
				&lt;br /&gt;
				// Settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;invokes&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				], 	 &lt;br /&gt;
			},&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; // require &amp;quot;eventStageTag&amp;quot; if have settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;SellOfferMgr_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;,&lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;SellOfferMgr&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessLogicalHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;output_intTesting_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;, &lt;br /&gt;
			},&lt;br /&gt;
			... &lt;br /&gt;
		] &lt;br /&gt;
	},&lt;br /&gt;
	... &lt;br /&gt;
] &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2886</id>
		<title>2025-02-20 - Integration Test examples</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2025-02-20_-_Integration_Test_examples&amp;diff=2886"/>
		<updated>2025-02-21T07:14:03Z</updated>

		<summary type="html">&lt;p&gt;Pong: Created page with &amp;quot;= Overview = :The example of properties in resources.json, events.json and tests.json for upload to S3 bucket  :Integration Test Config for config of each property in these files = resources.json = &amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt; {  	&amp;quot;Lambda&amp;quot;:{ 		&amp;quot;{FunctionNameHdrSqs}&amp;quot;:{  			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/FunctionNameHdrSqs.js&amp;quot;, 			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;,  			&amp;quot;functionName&amp;quot;:&amp;quot;FunctionNameHdrSqs&amp;quot;	// FunctionName is full name of function afte...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
:The example of properties in resources.json, events.json and tests.json for upload to S3 bucket &lt;br /&gt;
:[[Service - Integration Test Config|Integration Test Config]] for config of each property in these files&lt;br /&gt;
= resources.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
	&amp;quot;Lambda&amp;quot;:{&lt;br /&gt;
		&amp;quot;{FunctionNameHdrSqs}&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/FunctionNameHdrSqs.js&amp;quot;,&lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;FunctionNameHdrSqs&amp;quot;	// FunctionName is full name of function after deploy&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ProcessComplexFilterHdrSqs&amp;quot;:{ &lt;br /&gt;
			&amp;quot;localLacation&amp;quot;:&amp;quot;/src/ProcessComplexFilterHdrSqs.js&amp;quot;, &lt;br /&gt;
			&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
			&amp;quot;functionName&amp;quot;:&amp;quot;ProcessComplexFilterHdrSqs&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;Dynamodb&amp;quot;: {&lt;br /&gt;
		&amp;quot;filterMain&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;filterMain&amp;quot; &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;tableNameBB&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;tableNameBB&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= events.json =&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// Layout of file events.json &lt;br /&gt;
{ &lt;br /&gt;
	input:{}, &lt;br /&gt;
	output:{}, &lt;br /&gt;
	dynamodb:{}&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
// An example of naming an eventTag for each event&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},					            // for input &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},				        // for output&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;: {}  // for dynamodb&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot; is called &amp;lt;u&amp;gt;inputEventTag&amp;lt;/u&amp;gt;&lt;br /&gt;
* for &amp;lt;u&amp;gt;outputEventTag&amp;lt;/u&amp;gt; is '''Output_{inputEvenTag}'''&lt;br /&gt;
:: eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
* for dynamodb is '''{inputEventTag}__dynamodb_{action}_{tablename}'''&lt;br /&gt;
:: eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
: &amp;lt;u&amp;gt;'''{action}''' is only ''put'' or ''update'', and in case of ''update'' must only test the last step of update &amp;lt;/u&amp;gt;&lt;br /&gt;
== Sns/Sqs, Dsq and Invoke ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of Sns/Sqs, Dsq and Invoke&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
				&amp;quot;testValueMatches&amp;quot;: false,  // no child properties will be tested &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
					} &lt;br /&gt;
				} &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
				&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
					&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
				] &lt;br /&gt;
			} &lt;br /&gt;
		}, &lt;br /&gt;
		&lt;br /&gt;
		// settings for Sns/Sqs&lt;br /&gt;
		&amp;quot;snsSqsTrigger&amp;quot;: true,        // set when event is initial stage of Sns&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: {        // optional &lt;br /&gt;
			&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
			&amp;quot;properties&amp;quot;: { &lt;br /&gt;
				&amp;quot;msgTag&amp;quot;: { 	      // require &lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;msgTag&amp;quot; &lt;br /&gt;
				}, &lt;br /&gt;
				&amp;quot;propertyD&amp;quot;: { &lt;br /&gt;
					&amp;quot;forStageMatching&amp;quot;: true,     // default: false, whether this property is used to match stage config during integration tests&lt;br /&gt;
					&amp;quot;testValueMatches&amp;quot;: false,    // default: true, whether value of this property is checked when performing test&lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;valuePropertyD&amp;quot; &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
		&lt;br /&gt;
		// settings for Dsq&lt;br /&gt;
		// DirectSqs set in HdrDsq if have messageAttributeValidatorSchema and required property in messageAttributes eg: messageTag &lt;br /&gt;
		&amp;quot;DirectSqs&amp;quot;: true, 			// set when event is initial stage of Dsq&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: { 	    // optional  &lt;br /&gt;
			&amp;quot;messageTag&amp;quot;: { &lt;br /&gt;
				&amp;quot;DataType&amp;quot;: &amp;quot;String&amp;quot;, &lt;br /&gt;
				&amp;quot;StringValue&amp;quot;: &amp;quot;messageAttributes_is&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}&lt;br /&gt;
			...&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== API ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of API&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;forstageMatvhing&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
						&amp;quot;properties&amp;quot;: { &lt;br /&gt;
							&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
								&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
							} &lt;br /&gt;
						} &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
						&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
							&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
						] &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}&lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;&lt;br /&gt;
					}, &lt;br /&gt;
					...&lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}  &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Dynamodb ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of Dynamodb &lt;br /&gt;
{&lt;br /&gt;
	{inputEventTag}__dynamodb_{action}_{tablename}: { // eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			(property is update)... &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;keyValues&amp;quot;: { &lt;br /&gt;
			{partitionKey}: &amp;quot;4865d2451a621cb7f71225cfabf2df4f98e20801 &amp;quot;, &lt;br /&gt;
			{sortKey}: &amp;quot;cdb170690571df7522b3850920fce02507c077ee&amp;quot; &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== NoRetryError ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of NoRetryError output &lt;br /&gt;
{&lt;br /&gt;
	Output_{inputEvenTag}: { // eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;errorMessage&amp;quot;: { &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;Not found schema of {serviceTag:TestGraphHandler, objectType:notFoundObjType}&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= tests.json = &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example test &lt;br /&gt;
[ &lt;br /&gt;
	{ &lt;br /&gt;
		&amp;quot;integrationTestTag&amp;quot;: &amp;quot;test_S3_ByDev&amp;quot;, &lt;br /&gt;
		&amp;quot;productionSafe&amp;quot;: false, &lt;br /&gt;
		&amp;quot;errorIfStageUnderfined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;errorIfInvokeUnderfined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;stages&amp;quot;: [&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;initialStage&amp;quot;: true, &lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessComplexFilterHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;snsServiceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;,     // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke &lt;br /&gt;
				&amp;quot;snsTopic&amp;quot;: &amp;quot;InProcessComplexFilter&amp;quot;,  // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; direct invoke&lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;Output_Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;BatchSqsMessages&amp;quot;: true&lt;br /&gt;
				&lt;br /&gt;
				// Settings if have test dynamodb&lt;br /&gt;
				&amp;quot;dynamodbOutputEventIdentifiers&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
						&amp;quot;resourceName&amp;quot;: &amp;quot;FilterMain&amp;quot;, &lt;br /&gt;
						&amp;quot;eventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic_dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				],&lt;br /&gt;
				&lt;br /&gt;
				&lt;br /&gt;
				// Settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;invokes&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				], 	 &lt;br /&gt;
			},&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; // require &amp;quot;eventStageTag&amp;quot; if have settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;SellOfferMgr_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;,&lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;SellOfferMgr&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessLogicalHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;output_intTesting_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;, &lt;br /&gt;
			},&lt;br /&gt;
			... &lt;br /&gt;
		] &lt;br /&gt;
	},&lt;br /&gt;
	... &lt;br /&gt;
] &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Developer_guide_for_making_Integration_Tests&amp;diff=2885</id>
		<title>Developer guide for making Integration Tests</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Developer_guide_for_making_Integration_Tests&amp;diff=2885"/>
		<updated>2025-02-21T07:12:22Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
:How to create folder and file in S3 bucket for prepare integrationTest&lt;br /&gt;
:[[Service - Integration Test Config|Integration Test Config]] for config of property in file resources.json, events.json and tests.json&lt;br /&gt;
= Setup integrationtest =&lt;br /&gt;
== Create folder ==&lt;br /&gt;
: Create your folder in bucket '''integrationtest-config-us-east-2/test_config/{&amp;lt;u&amp;gt;your folder name&amp;lt;/u&amp;gt;}''' in S3 bucket&lt;br /&gt;
:* '''{&amp;lt;u&amp;gt;your folder name&amp;lt;/u&amp;gt;}''' maybe is your nickname eg. jamesDev, nicoleDev&lt;br /&gt;
:* Can create a subfolder in your folder for each test case or service that you want to test&lt;br /&gt;
:: eg. - integrationtest-config-us-east-2/test_config/jamesDev/SellOfferManager&lt;br /&gt;
::: - integrationtest-config-us-east-2/test_config/jamesDev/ProcessLogical&lt;br /&gt;
::: - integrationtest-config-us-east-2/test_config/jamesDev/SellOfferManager/ProcessLogical&lt;br /&gt;
== Create file ==&lt;br /&gt;
# Create a file in a folder that is used for testing&lt;br /&gt;
#* ''resources.json / events.json / tests.json''&lt;br /&gt;
#* '''resources.json''' and '''events.json''' is json object&lt;br /&gt;
#* '''tests.json''' is json array of objects&lt;br /&gt;
#* Can create these files in a SharedResource folder and share it with another test for collaborative usage&lt;br /&gt;
#:: eg. integrationtest-config-us-east-2/test_config/jamesDev/SellOfferManager/SharedResource/resources.json&lt;br /&gt;
#* In the main prefix for testing, you don’t need to upload every file. If you have a file in SharedResource, you can skip uploading it &lt;br /&gt;
## '''resources.json'''&lt;br /&gt;
##* Syntax; [[Service_-_Integration_Test_Config#resources.js Syntax|resources Syntax]]&lt;br /&gt;
## '''events.json'''&lt;br /&gt;
##* Syntax; [[Service_-_Integration_Test_Config#events/*.js_Syntax|events Syntax]]&lt;br /&gt;
##* Not use &amp;lt;code&amp;gt;&amp;lt;s&amp;gt;isObject&amp;lt;/s&amp;gt;&amp;lt;/code&amp;gt;. Use &amp;lt;code&amp;gt;properties&amp;lt;/code&amp;gt; instead, if is an object will look for child properties&lt;br /&gt;
##* Not use &amp;lt;code&amp;gt;&amp;lt;s&amp;gt;isStringSet&amp;lt;/s&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
##* If have many stage, Set &amp;lt;code&amp;gt;initialStage&amp;lt;/code&amp;gt; is true only initial stage. Next stage not set this setting&lt;br /&gt;
##* If &amp;lt;code&amp;gt;snsSqsTrigger&amp;lt;/code&amp;gt; is true but not set snsServiceName or snsTopic --&amp;gt; direct invoke &lt;br /&gt;
##* If &amp;lt;code&amp;gt;DirectSqs&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;BatchSqsMessages&amp;lt;/code&amp;gt; is true --&amp;gt; direct invoke&lt;br /&gt;
##* If &amp;lt;code&amp;gt;snsSqsTrigger&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;DirectSqs&amp;lt;/code&amp;gt; is false or not set --&amp;gt; direct invoke&lt;br /&gt;
##* &amp;lt;code&amp;gt;forStageMatching&amp;lt;/code&amp;gt; &amp;lt;u&amp;gt;Use in test important parameter&amp;lt;/u&amp;gt;&lt;br /&gt;
##* &amp;lt;code&amp;gt;testValueMatches&amp;lt;/code&amp;gt; &amp;lt;u&amp;gt;For random parameter eg. timeStamp or uuid&amp;lt;/u&amp;gt;&lt;br /&gt;
##* If value in array or number or string is &amp;lt;u&amp;gt;random&amp;lt;/u&amp;gt;. '''Use testValueMatches: true not set forStageMatching: true'''  &lt;br /&gt;
##* Multiple eventTags can be added to a single file&lt;br /&gt;
##* Have 6 types of events; Dynamodb, Sns/Sqs, Dsq, Invoke, API and NoRetryError &lt;br /&gt;
## '''tests.json'''&lt;br /&gt;
##* Syntax; [[Service_-_Integration_Test_Config#tests/*.js_Syntax|tests Syntax]]&lt;br /&gt;
##* Not use &amp;lt;code&amp;gt;&amp;lt;s&amp;gt;dynamoDbOutput&amp;lt;/s&amp;gt;&amp;lt;/code&amp;gt;. Use &amp;lt;code&amp;gt;dynamodbOutputEventIdentifiers&amp;lt;/code&amp;gt; instead&lt;br /&gt;
# Upload file to S3 bucket by batchscript or upload to S3 bucket directly&lt;br /&gt;
#:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash &lt;br /&gt;
set -u &lt;br /&gt;
&lt;br /&gt;
localPath=&amp;quot;/home/dogsrcute/Programming mint/Fixed_Integrationtest/PluemDev/paginate/2stages&amp;quot; &lt;br /&gt;
bucketName=s3://integrationtest-config-us-east-2/test_config/mintDev/fixed/PluemDev/2stages/ &lt;br /&gt;
&lt;br /&gt;
aws s3 cp &amp;quot;$localPath&amp;quot; &amp;quot;$bucketName&amp;quot; --recursive &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
#* localPath is part to files for upload &lt;br /&gt;
#* bucketName is your bucket&lt;br /&gt;
== Type of parameter ==&lt;br /&gt;
: Parameter type for create property in events.json&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
object: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	properties: {} &lt;br /&gt;
}, &lt;br /&gt;
array: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	eventValue: [] &lt;br /&gt;
}, &lt;br /&gt;
number: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	value: 1111 &lt;br /&gt;
}, &lt;br /&gt;
string: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	value: &amp;quot;hi&amp;quot; &lt;br /&gt;
}, &lt;br /&gt;
boolean: { &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	value: true &lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
// Case empty &lt;br /&gt;
EmptyString:{ &lt;br /&gt;
	value: &amp;quot;&amp;quot; &lt;br /&gt;
}, &lt;br /&gt;
EmptyArray:{ &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	eventValue: [] &lt;br /&gt;
}, &lt;br /&gt;
EmptyObject:{ &lt;br /&gt;
	forStageMatching: true, &lt;br /&gt;
	value: {} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= Test =&lt;br /&gt;
# create params for invoke &amp;lt;u&amp;gt;IntTestingTestInitiateIntegrationTestHdrInv&amp;lt;/u&amp;gt; function&lt;br /&gt;
#:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
	&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;,  &lt;br /&gt;
	&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;,  &lt;br /&gt;
	&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessComplexFilterHdrSqs&amp;quot;,  &lt;br /&gt;
	&amp;quot;integrationTestTags&amp;quot;: [ &lt;br /&gt;
		&amp;quot;test_S3_ByDev&amp;quot;  &lt;br /&gt;
	],  &lt;br /&gt;
	&amp;quot;bucketName&amp;quot;: &amp;quot;integrationtest-config-us-east-2&amp;quot;,  &lt;br /&gt;
	&amp;quot;bucketPrefix&amp;quot;:[  &lt;br /&gt;
		&amp;quot;test_config/mintDev/basicCase/&amp;quot;, &lt;br /&gt;
		&amp;quot;test_config/mintDev/SharedResource/&amp;quot;  &lt;br /&gt;
	]  &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Find results in IntTestingTestTestRecord table&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Developer_guide_for_making_Integration_Tests&amp;diff=2884</id>
		<title>Developer guide for making Integration Tests</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Developer_guide_for_making_Integration_Tests&amp;diff=2884"/>
		<updated>2025-02-18T08:39:13Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
= Setup integrationtest =&lt;br /&gt;
=== Step 1; Create object in bucket ===&lt;br /&gt;
# Create your folder in bucket '''integrationtest-config-us-east-2/test_config/{&amp;lt;u&amp;gt;your folder name&amp;lt;/u&amp;gt;}''' in S3 bucket&lt;br /&gt;
#* '''{&amp;lt;u&amp;gt;your folder name&amp;lt;/u&amp;gt;}''' maybe is your nickname eg. jamesDev, nicoleDev&lt;br /&gt;
#* Can create a subfolder in your folder for each test case or service that you want to test&lt;br /&gt;
#:: eg. - integrationtest-config-us-east-2/test_config/jamesDev/SellOfferManager&lt;br /&gt;
#::: - integrationtest-config-us-east-2/test_config/jamesDev/ProcessLogical&lt;br /&gt;
#::: - integrationtest-config-us-east-2/test_config/jamesDev/SellOfferManager/ProcessLogical&lt;br /&gt;
# Create a file in a folder that is used for testing&lt;br /&gt;
#* ''resources.json / events.json / tests.json''&lt;br /&gt;
#* '''resources.json''' and '''events.json''' is json object&lt;br /&gt;
#* '''tests.json''' is json array of objects&lt;br /&gt;
#* Can create these files in a SharedResource folder and share it with another test for collaborative usage&lt;br /&gt;
#:: eg. integrationtest-config-us-east-2/test_config/jamesDev/SellOfferManager/SharedResource/resources.json&lt;br /&gt;
#* In the main prefix for testing, you don’t need to upload every file. If you have a file in SharedResource, you can skip uploading it &lt;br /&gt;
## '''resources.json'''&lt;br /&gt;
##* Have 2 main object keys is ''Lambda'' and ''Dynamodb'' &lt;br /&gt;
##:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
	&amp;quot;Lambda&amp;quot;:{&lt;br /&gt;
		&amp;quot;{FunctionNameHdrSqs}&amp;quot;:{ &lt;br /&gt;
		&amp;quot;localLacation&amp;quot;:&amp;quot;/src/FunctionNameHdrSqs.js&amp;quot;,&lt;br /&gt;
		&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
		&amp;quot;functionName&amp;quot;:&amp;quot;FunctionNameHdrSqs&amp;quot;	// FunctionName is full name of function after deploy&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;ProcessComplexFilterHdrSqs&amp;quot;:{ &lt;br /&gt;
		&amp;quot;localLacation&amp;quot;:&amp;quot;/src/ProcessComplexFilterHdrSqs.js&amp;quot;, &lt;br /&gt;
		&amp;quot;localHandler&amp;quot;:&amp;quot;main&amp;quot;, &lt;br /&gt;
		&amp;quot;functionName&amp;quot;:&amp;quot;ProcessComplexFilterHdrSqs&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;Dynamodb&amp;quot;: {&lt;br /&gt;
		&amp;quot;filterMain&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;filterMain&amp;quot; &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;tableNameBB&amp;quot;:{ &lt;br /&gt;
			&amp;quot;tableName&amp;quot;:&amp;quot;tableNameBB&amp;quot; &lt;br /&gt;
		},&lt;br /&gt;
		... &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
## '''events.json'''&lt;br /&gt;
##* Multiple eventTags can be added to a single file&lt;br /&gt;
##* Have 6 types of events; Dynamodb, Sns/Sqs, Dsq, Invoke, API and NoRetryError &lt;br /&gt;
##:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// Layout of file events.json &lt;br /&gt;
{ &lt;br /&gt;
	input:{}, &lt;br /&gt;
	output:{}, &lt;br /&gt;
	dynamodb:{}&lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
// An example of naming an eventTag for each event&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},					          // for input &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: {},				      // for output&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;: {}  // for dynamodb&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
##* &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot; is called '''{inputEventTag}'''&lt;br /&gt;
##* for {outputEventTag} is '''Output_{inputEvenTag}'''&lt;br /&gt;
##:: eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
##* for dynamodb is '''{inputEventTag}__dynamodb_{action}_{tablename}'''&lt;br /&gt;
##:: eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
##: &amp;lt;u&amp;gt;'''{action}''' is only ''put'' or ''update'', and in case of ''update'', must only test the last step of update &amp;lt;/u&amp;gt;&lt;br /&gt;
##:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of Sns/Sqs, Dsq and Invoke&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
				&amp;quot;testValueMatches&amp;quot;: false,  // no child properties will be tested &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
					} &lt;br /&gt;
				} &lt;br /&gt;
			}, &lt;br /&gt;
			&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
				&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
					&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
					&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
				] &lt;br /&gt;
			} &lt;br /&gt;
		}, &lt;br /&gt;
		&lt;br /&gt;
		// settings for Sns/Sqs&lt;br /&gt;
		&amp;quot;snsSqsTrigger&amp;quot;: true,        // set when event is initial stage of Sns&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: {        // optional &lt;br /&gt;
			&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
			&amp;quot;properties&amp;quot;: { &lt;br /&gt;
				&amp;quot;msgTag&amp;quot;: { 	      // require &lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;msgTag&amp;quot; &lt;br /&gt;
				}, &lt;br /&gt;
				&amp;quot;propertyD&amp;quot;: { &lt;br /&gt;
					&amp;quot;forStageMatching&amp;quot;: true,     // default: false, whether this property is used to match stage config during integration tests&lt;br /&gt;
					&amp;quot;testValueMatches&amp;quot;: false,    // default: true, whether value of this property is checked when performing test&lt;br /&gt;
					&amp;quot;value&amp;quot;: &amp;quot;valuePropertyD&amp;quot; &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
		&lt;br /&gt;
		// settings for Dsq&lt;br /&gt;
		// DirectSqs set in HdrDsq if have messageAttributeValidatorSchema and required property in messageAttributes eg: messageTag &lt;br /&gt;
		&amp;quot;DirectSqs&amp;quot;: true, 			// set when event is initial stage of Dsq&lt;br /&gt;
		&amp;quot;messageAttributes&amp;quot;: { 	    // optional  &lt;br /&gt;
			&amp;quot;messageTag&amp;quot;: { &lt;br /&gt;
				&amp;quot;DataType&amp;quot;: &amp;quot;String&amp;quot;, &lt;br /&gt;
				&amp;quot;StringValue&amp;quot;: &amp;quot;messageAttributes_is&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;: { &lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true,        &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
				&amp;quot;forStageMatching&amp;quot;: true,  &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
			}&lt;br /&gt;
			...&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
##* &amp;lt;code&amp;gt;forStageMatching&amp;lt;/code&amp;gt; default: false, whether this property is used to match stage config during integration tests. &amp;lt;u&amp;gt; Use in test importent parameter &amp;lt;/u&amp;gt;&lt;br /&gt;
##* &amp;lt;code&amp;gt;testValueMatches&amp;lt;/code&amp;gt;   default: true, whether value of this property is checked when performing test. &amp;lt;u&amp;gt;For random eg. timeStamp or uuid &amp;lt;/u&amp;gt;&lt;br /&gt;
##* If value in array or number or string is &amp;lt;u&amp;gt;random&amp;lt;/u&amp;gt;. '''Use testValueMatches: true not set forStageMatching: true'''  &lt;br /&gt;
##:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example input and output event of API&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;forstageMatvhing&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;, &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyB&amp;quot;: { &lt;br /&gt;
						&amp;quot;properties&amp;quot;: { &lt;br /&gt;
							&amp;quot;propertyB1&amp;quot;: { &lt;br /&gt;
								&amp;quot;value&amp;quot;: &amp;quot;valuePropertyB1&amp;quot;, &lt;br /&gt;
							} &lt;br /&gt;
						} &lt;br /&gt;
					}, &lt;br /&gt;
					&amp;quot;propertyC&amp;quot;: { &lt;br /&gt;
						&amp;quot;eventValue&amp;quot;: [ &lt;br /&gt;
							&amp;quot;valuePropertyC1&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC2&amp;quot;, &lt;br /&gt;
							&amp;quot;valuePropertyC3&amp;quot;, &lt;br /&gt;
						] &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}&lt;br /&gt;
	}, &lt;br /&gt;
	&amp;quot;Output_LambdaFunctionHdrApi_input__pass_notTestAuthorizer&amp;quot;: { &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;body&amp;quot;: { &lt;br /&gt;
				&amp;quot;properties&amp;quot;: { &lt;br /&gt;
					&amp;quot;propertyA&amp;quot;: { &lt;br /&gt;
						&amp;quot;testValueMatches&amp;quot;: false, &lt;br /&gt;
						&amp;quot;value&amp;quot;: &amp;quot;valueProperty1&amp;quot;&lt;br /&gt;
					}, &lt;br /&gt;
					...&lt;br /&gt;
				} &lt;br /&gt;
			} &lt;br /&gt;
		}  &lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
##:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of Dynamodb &lt;br /&gt;
{&lt;br /&gt;
	{inputEventTag}__dynamodb_{action}_{tablename}: { // eg. &amp;quot;Complexfilter_ProcessComplexfilter_logical_basic__dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			(property is update)... &lt;br /&gt;
		}, &lt;br /&gt;
		&amp;quot;keyValues&amp;quot;: { &lt;br /&gt;
			{partitionKey}: &amp;quot;4865d2451a621cb7f71225cfabf2df4f98e20801 &amp;quot;, &lt;br /&gt;
			{sortKey}: &amp;quot;cdb170690571df7522b3850920fce02507c077ee&amp;quot; &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
##:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example event of NoRetryError output &lt;br /&gt;
{&lt;br /&gt;
	Output_{inputEvenTag}: { // eg. &amp;quot;Output_Complexfilter_ProcessComplexfilter_logical_basic&amp;quot;&lt;br /&gt;
		&amp;quot;forStageMatching&amp;quot;: true, &lt;br /&gt;
		&amp;quot;properties&amp;quot;: { &lt;br /&gt;
			&amp;quot;errorMessage&amp;quot;: { &lt;br /&gt;
				&amp;quot;value&amp;quot;: &amp;quot;Not found schema of {serviceTag:TestGraphHandler, objectType:notFoundObjType}&amp;quot; &lt;br /&gt;
			} &lt;br /&gt;
		} &lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
## '''tests.json'''&lt;br /&gt;
##:&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// An example test &lt;br /&gt;
[ &lt;br /&gt;
	{ &lt;br /&gt;
		&amp;quot;integrationTestTag&amp;quot;: &amp;quot;test_S3_ByDev&amp;quot;, &lt;br /&gt;
		&amp;quot;productionSafe&amp;quot;: false, &lt;br /&gt;
		&amp;quot;errorIfStageUnderfined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;errorIfInvokeUnderfined&amp;quot;: false, &lt;br /&gt;
		&amp;quot;stages&amp;quot;: [&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;initialStage&amp;quot;: true, &lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessComplexFilterHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;snsServiceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;,     // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; will direct invoke &lt;br /&gt;
				&amp;quot;snsTopic&amp;quot;: &amp;quot;InProcessComplexFilter&amp;quot;,  // if snsSqsTrigger = true but not set snsServiceName or snsTopic --&amp;gt; will direct invoke&lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;Output_Complexfiter_ProcessComplexfiter_logical_basic&amp;quot;, &lt;br /&gt;
				&lt;br /&gt;
				&lt;br /&gt;
				// Settings if have test dynamodb&lt;br /&gt;
				&amp;quot;dynamodbOutputEventIdentifiers&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;serviceName&amp;quot;: &amp;quot;ComplexFilter&amp;quot;, &lt;br /&gt;
						&amp;quot;resourceName&amp;quot;: &amp;quot;FilterMain&amp;quot;, &lt;br /&gt;
						&amp;quot;eventTag&amp;quot;: &amp;quot;Complexfiter_ProcessComplexfiter_logical_basic_dynamodb_update_FilterMain&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				],&lt;br /&gt;
				&lt;br /&gt;
				&lt;br /&gt;
				// Settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;invokes&amp;quot;: [ &lt;br /&gt;
					{ &lt;br /&gt;
						&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; &lt;br /&gt;
					},&lt;br /&gt;
					... &lt;br /&gt;
				], 	 &lt;br /&gt;
			},&lt;br /&gt;
			{ &lt;br /&gt;
				&amp;quot;eventStageTag&amp;quot;: &amp;quot;GraphHandler_GetNodeV2_NomalCase&amp;quot; // require &amp;quot;eventStageTag&amp;quot; if have settings multiple stage for invoke&lt;br /&gt;
				&amp;quot;inputEventTag&amp;quot;: &amp;quot;SellOfferMgr_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;,&lt;br /&gt;
				&amp;quot;serviceName&amp;quot;: &amp;quot;SellOfferMgr&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceType&amp;quot;: &amp;quot;Lambda&amp;quot;, &lt;br /&gt;
				&amp;quot;resourceName&amp;quot;: &amp;quot;ProcessLogicalHdrSqs&amp;quot;, &lt;br /&gt;
				&amp;quot;outputEventTag&amp;quot;: &amp;quot;output_intTesting_ProcessLogical_logical_integrationTesting_returnObject&amp;quot;, &lt;br /&gt;
			},&lt;br /&gt;
			... &lt;br /&gt;
		] &lt;br /&gt;
	},&lt;br /&gt;
	... &lt;br /&gt;
] &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2025-02-16_-_Supply_Chain_Transform_Units_examples&amp;diff=2883</id>
		<title>2025-02-16 - Supply Chain Transform Units examples</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2025-02-16_-_Supply_Chain_Transform_Units_examples&amp;diff=2883"/>
		<updated>2025-02-18T08:29:59Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Calculate cost of outputUnit from many inputUnits =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	properties: {&lt;br /&gt;
		inputUnits: [&lt;br /&gt;
			{&lt;br /&gt;
				unitTypeId: &amp;quot;fabric&amp;quot;,&lt;br /&gt;
				numberOfUnits: 1,&lt;br /&gt;
				calculatedProperties:{&lt;br /&gt;
					unitTypeTrackedPropertyId(remainingYards): {&lt;br /&gt;
						integerOnly: false,&lt;br /&gt;
						quantityReductionSetting: &amp;quot;clearPerUnit&amp;quot;,&lt;br /&gt;
						valueUsePerOutputUnit: 5.7&lt;br /&gt;
					},&lt;br /&gt;
				},				&lt;br /&gt;
			}, &lt;br /&gt;
			{&lt;br /&gt;
				unitTypeId: &amp;quot;bagOfButtons&amp;quot;,&lt;br /&gt;
				numberOfUnits: 2,&lt;br /&gt;
				calculatedProperties:{&lt;br /&gt;
					unitTypeTrackedPropertyId(remainingButtons): {&lt;br /&gt;
						integerOnly: true,&lt;br /&gt;
						quantityReductionSetting: &amp;quot;clearPerUnit&amp;quot;,&lt;br /&gt;
						valueUsePerOutputUnit: 4&lt;br /&gt;
					},&lt;br /&gt;
				},&lt;br /&gt;
			}, &lt;br /&gt;
			//..&lt;br /&gt;
		],&lt;br /&gt;
		outputUnits: [&lt;br /&gt;
			{&lt;br /&gt;
				unitTypeId: &amp;quot;shirt&amp;quot;,&lt;br /&gt;
				numberOfUnits: 2,&lt;br /&gt;
				calculatedProperties: {&lt;br /&gt;
					unitTypeTrackedPropertyId(shirtCost): {&lt;br /&gt;
						calculationType: &amp;quot;calculateValue&amp;quot;,&lt;br /&gt;
						integerOnly: false,&lt;br /&gt;
						combinationEquation: &amp;quot;[yardCost]+[buttonCost]+[stitchCost]&amp;quot;, // not needed as default is to sum all calculateByProperties&lt;br /&gt;
						calculateByProperties: {&lt;br /&gt;
							yardCost: {&lt;br /&gt;
								calculation: &amp;quot;[valueUsePerOutputUnit:{inputUnitTypeId(fabric)}:{unitTypeTrackedPropertyId(remainingYards)}]*[unitTypeTrackedProperty:{inputUnitTypeId(fabric)}:{unitTypeTrackedPropertyId(costPerYard)}]&amp;quot;,&lt;br /&gt;
							},&lt;br /&gt;
							buttonCost: {&lt;br /&gt;
								calculation: &amp;quot;[valueUsePerOutputUnit:{inputUnitTypeId(bagOfButtons)}:{unitTypeTrackedPropertyId(remainingButtons)}]*([unitTypeTrackedProperty:{inputUnitTypeId(fabric)}:{unitTypeTrackedPropertyId(costPerBag)}]/5)&amp;quot;,&lt;br /&gt;
								round: &amp;quot;round&amp;quot;,&lt;br /&gt;
							},&lt;br /&gt;
							stitchCost: {&lt;br /&gt;
								defaultValue: &amp;quot;30&amp;quot;,&lt;br /&gt;
							},&lt;br /&gt;
						},&lt;br /&gt;
					}, &lt;br /&gt;
					//..&lt;br /&gt;
				}&lt;br /&gt;
			}, &lt;br /&gt;
			//..&lt;br /&gt;
		],&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* fabric has costPerYard property&lt;br /&gt;
* bagOfButtons has costPerBag property&lt;br /&gt;
* each bagOfButtons has 5 buttons in it, so divide costPerBag/5 to get per button cost&lt;br /&gt;
&lt;br /&gt;
With default settings above example:&lt;br /&gt;
* initial state: fabric has 100 remainingYards, costPerYard = 3&lt;br /&gt;
* initial state: 2 bagOfButtons, each has 5 remainingButtons, costPerBag = 30&lt;br /&gt;
* reduce fabric remainingYards by 11.4 (1 unit of fabric must have enough yard remaining, but additional units could be added to make up needed yardage)&lt;br /&gt;
* reduce bagOfButtons 1 unit to 0 remainingButtons, and another to 2 remainingButtons (if bagOfButtons.inactiveIfZero is true 0 remainingButtons will be set to inactive)&lt;br /&gt;
* if bagOfButtons quantityReductionSetting was set to &amp;quot;spread&amp;quot;, each bagOfButtons would have 1 remainingButtons&lt;br /&gt;
* create 2 shirts&lt;br /&gt;
* each shirt valueUsePerOutputUnit for fabric.remainingYards will be 5.7 and bagOfButtons.remainingButtons will be 4&lt;br /&gt;
* yardCost = 5.7*3 = 17.1&lt;br /&gt;
* buttonCost = 4*(20/5) = 24&lt;br /&gt;
* shirtCost = 17.1+24+30 = 71.1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2025-02-16]]&lt;br /&gt;
[[Category:Working documents - Transform Units| 2025-02-16]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2024-11-28_-_OrderPrice_using_linkPath_flows&amp;diff=2770</id>
		<title>2024-11-28 - OrderPrice using linkPath flows</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2024-11-28_-_OrderPrice_using_linkPath_flows&amp;diff=2770"/>
		<updated>2024-11-28T05:55:35Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Sell Offer Manager]]&lt;br /&gt;
&lt;br /&gt;
= Order subtotal =&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* can receive multiple sellOfferQuantities&lt;br /&gt;
* will validate that all sellOffers in sellOfferQuantities share the same DeliveryMethodLink (and PaymentMethodLink)&lt;br /&gt;
&lt;br /&gt;
== Process ==&lt;br /&gt;
&lt;br /&gt;
=== Inital flow ===&lt;br /&gt;
&lt;br /&gt;
* SellOfferManager recieves request with multiple sellOfferQuantities&lt;br /&gt;
* calculates orderQty&lt;br /&gt;
* per sellOfferId send to SellOffer Handler service (SellOfferStandard) with sellOfferQty and orderQty&lt;br /&gt;
* SellOfferStandard passes to SellOfferPrices service&lt;br /&gt;
* create two aggregated SortResult requests, one for sellOfferQty, another for orderQty&lt;br /&gt;
&lt;br /&gt;
=== Base SortResult request ===&lt;br /&gt;
&lt;br /&gt;
* objType: SellOfferPricing&lt;br /&gt;
* translate filter element for SellOfferId&lt;br /&gt;
* logical filter element for sellOfferPricing.type = sellOfferQty|orderQty&lt;br /&gt;
* requiredData: linkPath&lt;br /&gt;
* sortField: Price (from SellOfferPrice objType)&lt;br /&gt;
&lt;br /&gt;
=== LinkPath ===&lt;br /&gt;
&lt;br /&gt;
* field: Price (from SellOfferPrice objType)&lt;br /&gt;
* one LinkPath step, to SellOfferPrice&lt;br /&gt;
* comparison: high&lt;br /&gt;
* comparisonValue: sellOfferQty|orderQty&lt;br /&gt;
* sort: upToValue&lt;br /&gt;
&lt;br /&gt;
=== After base SortResult complete ===&lt;br /&gt;
&lt;br /&gt;
* aggregate minimum Price&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2024-11-28]]&lt;br /&gt;
[[Category:Working documents - Sell Offer Manager| 2024-11-28]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2024-11-28_-_SearchResult_linkPath_comparison_setting&amp;diff=2769</id>
		<title>2024-11-28 - SearchResult linkPath comparison setting</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2024-11-28_-_SearchResult_linkPath_comparison_setting&amp;diff=2769"/>
		<updated>2024-11-28T05:55:06Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Search Results]]&lt;br /&gt;
&lt;br /&gt;
= Overview =&lt;br /&gt;
&lt;br /&gt;
Performs a comparison on the results of a SortResult request at this link step. Comparison can be &amp;gt;/&amp;lt;/&amp;gt;=/&amp;lt;=/high/low/highLow and can result in multiple identifiers being saved into IntermediateIds table.&lt;br /&gt;
&lt;br /&gt;
= Idea =&lt;br /&gt;
&lt;br /&gt;
* LinkStep sets comparison property with value one of: &amp;gt;, &amp;lt;, &amp;gt;=, &amp;lt;=, high, low, highLow.&lt;br /&gt;
&lt;br /&gt;
* LinkStep sets comparisonValue property.&lt;br /&gt;
&lt;br /&gt;
* if LinkStep has comparison then it must also have sortFields settings.&lt;br /&gt;
&lt;br /&gt;
* if comparison is &amp;gt;/&amp;lt;/&amp;gt;=/&amp;lt;= flow is the same as combined+aggregate because results for all parent identifiers can be mixed together before performing the comparison, adding combined=true will make no difference to the results.&lt;br /&gt;
&lt;br /&gt;
* if comparison is high/low/highLow then must also consider combined setting, if combined=true then same as combined+aggregate. If combined=false then must perform comparison for each parent identifier (multiple SortResult requests and Comparison result for each gets saved into IntermediateId table).&lt;br /&gt;
&lt;br /&gt;
* validation in link path: if is final step and has comparison must set combined=true, unless comparison is high or low and hasMany=false (which only return one id).&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2024-11-28]]&lt;br /&gt;
[[Category:Working documents - Search Results| 2024-11-28]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Sell_Offer_Price&amp;diff=2757</id>
		<title>Service - Sell Offer Price</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Sell_Offer_Price&amp;diff=2757"/>
		<updated>2024-11-19T05:41:03Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Standard pricing config that can be shared by many sellOffers. Can create other pricing services in the future, each SellOffer Handler service chooses which pricing service it uses (or many if it wants).&lt;br /&gt;
&lt;br /&gt;
A user can point their sellOffer standard to any sellOfferPricing, even ones created by other users, but only the user who created the sellOfferPrice can change it.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/izara-market-products/izara-market-products-sell-offer-price/&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== sellOfferPricing ===&lt;br /&gt;
&lt;br /&gt;
* Groups many sellOfferPrice together&lt;br /&gt;
* sellOfferPricing have only one priceType of ''sellOfferQuantity'' or ''orderQuantity'' or ''orderValue''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;sellOfferPricing&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
	complexFilterServiceTag: &amp;quot;ComplexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 						// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 					// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},	 &lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		sellOfferPricingId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		priceType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		minimumQuantity: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;sellOfferPricingId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; priceType&lt;br /&gt;
: ''sellOfferQuantity''|''orderQuantity''|''orderValue''&lt;br /&gt;
&lt;br /&gt;
=== sellOfferPrice ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;sellOfferPrice&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
	complexFilterServiceTag: &amp;quot;ComplexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 					// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 						// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 					// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},	 &lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		sellOfferPriceId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			randomOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		uptoValue: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		price: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;sellOfferPriceId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== hasSellOfferPricing ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasSellOfferPricing&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: false,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;SellOfferManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;sellOffer&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;SellOfferPrice&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;sellOfferPricing&amp;quot;					&lt;br /&gt;
					},					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledSellOfferPricing ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledSellOfferPricing&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: false,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;SellOfferManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;sellOffer&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;SellOfferPrice&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;sellOfferPricing&amp;quot;					&lt;br /&gt;
					},					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== hasSellOfferPrice ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;hasSellOfferPrice&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: false,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;SellOfferPrice&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;sellOfferPricing&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;SellOfferPrice&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;sellOfferPrice&amp;quot;					&lt;br /&gt;
					},		&lt;br /&gt;
					requiredOnCreate: true,			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== disabledSellOfferPrice ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabledSellOfferPrice&amp;quot;: {&lt;br /&gt;
		fieldNames: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default = false&lt;br /&gt;
				canUpdate: false,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;SellOfferPrice&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;sellOfferPricing&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;SellOfferPrice&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;sellOfferPrice&amp;quot;					&lt;br /&gt;
					},		&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Sell Offer Price|Sell Offer Price]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Sell Offer Price]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2692</id>
		<title>Service - Activity Switchboard</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2692"/>
		<updated>2024-08-13T05:41:45Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Receives most messages sent within the entire project's serverless flow, allows services to register a set of triggers that are checked when each message is received, if all triggers pass a message is sent out for subscribed services.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/activity-switchboard/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== TriggerGroups ==&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: comes from: {receiverTag}_{uniqueId}&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service name eg. NotificationMgr&lt;br /&gt;
; uniqueId &lt;br /&gt;
: comes from receiver service, eg: {notificationId}, cannot include underscores&lt;br /&gt;
; triggers&lt;br /&gt;
: array of objects (DynamoDB list?)&lt;br /&gt;
: each element has triggerId, triggerGroupId, valueType, propertyName, propertyValueString or propertyValueNumber&lt;br /&gt;
&lt;br /&gt;
== Triggers ==&lt;br /&gt;
&lt;br /&gt;
; triggerId (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: string&lt;br /&gt;
: create by [[#triggerGroup]]&lt;br /&gt;
; valueType&lt;br /&gt;
: string &lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== triggerGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggers, all triggers for a trigger group must pass for the trigger group to pass&lt;br /&gt;
* no sort key&lt;br /&gt;
* if multiple values are sent for a trigger we consider this an &amp;quot;or&amp;quot; check, any of the values can match. We do this by saving each value as a separate trigger, then when checking a trigger groups child triggers we have a counter that enables a check if any one of the values matches&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;triggerGroup&amp;quot;,&lt;br /&gt;
	canDelete: true,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;TriggerGroups&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggers: {&lt;br /&gt;
			type: &amp;quot;array&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		receiverTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		uniqueId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldNames: [&amp;quot;receiverTag&amp;quot;, &amp;quot;uniqueId&amp;quot;],&lt;br /&gt;
			name: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: comes from: {receiverTag}_{uniqueId}&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service name eg. NotificationMgr&lt;br /&gt;
; uniqueId &lt;br /&gt;
: comes from receiver service, eg: {notificationId}, cannot include underscores&lt;br /&gt;
; triggers&lt;br /&gt;
: array of objects (DynamoDB list?)&lt;br /&gt;
: each element has triggerId, triggerGroupId, valueType, propertyName,  propertyValueString and propertyValueNumber&lt;br /&gt;
&lt;br /&gt;
=== trigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;trigger&amp;quot;,&lt;br /&gt;
	canDelete: true,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;Triggers&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;ActivitySwitchboard&amp;quot;,&lt;br /&gt;
			fromObjectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
: attributes is for message attributes&lt;br /&gt;
: property is from the data sent inside the message body&lt;br /&gt;
: hash property name and value so no ambiguity about underscores/spaces etc..&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: (sort key)&lt;br /&gt;
: created by [[#triggerGroup]]&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Efficiency =&lt;br /&gt;
&lt;br /&gt;
* Service will result in a large number of queries to Triggers table, every message will need to make a query for every field set as an activityTrigger&lt;br /&gt;
* Try to make this as efficient as possible&lt;br /&gt;
* To reduce number of queries made to Tiggers table we use the msgCfg for any message received&lt;br /&gt;
** MsgCfg sets which properties can be used as triggers, others are not queried&lt;br /&gt;
&lt;br /&gt;
= Handling msgCfgs =&lt;br /&gt;
&lt;br /&gt;
* msgCfgs get updated from [[Service - Message Config Manager|Message Config Manager]] service, we do this by subscribing to Message Config Manager's OutMsgCfgUpdate topic&lt;br /&gt;
* Only want to process messages from certain In/Out topics, it will be most topics but does not have to be all&lt;br /&gt;
* Set which topics to subscribe to by setting activitySwitchboard = true in msgCfg&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
* If more efficient can use cache for regular DynamoDB queries&lt;br /&gt;
* Could add other matching methods such as greater than or less than, in DynamoDB this might be more efficient to add as separate table with its own structure, eg: partition key is the field reference, sort key is the amount, then use sort key to return matching triggers. Danger of bad partitioning in DynamoDB (or hitting limits) due to large numbers of sort keys associated with one partition key&lt;br /&gt;
* could consider how to incorporate includes or checking within a set of options set in the trigger, again might need specialized table structure&lt;br /&gt;
* Might be able to optimise by using SNS &amp;gt; stream instead of SNS &amp;gt; SQS for all incomming messages&lt;br /&gt;
* One system level receiving service could be specialized logs, eg logs per service per user/per product/etc&lt;br /&gt;
* Topic name is not fixed part of the TriggersGroup structure, allow for trigger groups that are not connected to specific topics, but can pass messages from any topic that matches other triggers&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Activity Switchboard|Working_documents - Activity Switchboard]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Activity Switchboard]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=2691</id>
		<title>Service - User Contact Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=2691"/>
		<updated>2024-08-09T01:48:46Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Manages contact methods available to users. Records each users contacts.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/user-contact-manager/src/master/&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== userContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;userContact&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				        // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			        // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;userContactSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
					fieldName: &amp;quot;enable&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		userContactId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		methodTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		uniqueId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldNames: [&amp;quot;methodTag&amp;quot;, &amp;quot;uniqueId&amp;quot;],&lt;br /&gt;
			name: &amp;quot;userContactId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; userContactId&lt;br /&gt;
: comes from {methodTag}_{methods uniqueId, eg: emailUniqueId}&lt;br /&gt;
; methodTag&lt;br /&gt;
: comes from {serviceName} of receiver service &lt;br /&gt;
; uniqueId&lt;br /&gt;
: random UUID from receiver service&lt;br /&gt;
; userId&lt;br /&gt;
: id of user that create userContact &lt;br /&gt;
; enabled&lt;br /&gt;
: true|false&lt;br /&gt;
&lt;br /&gt;
== RefObjectRelationships ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// reference to relationshipTag in external service&lt;br /&gt;
[&lt;br /&gt;
  {&lt;br /&gt;
    objectType: &amp;quot;userContact&amp;quot;,                     // objectType in local service&lt;br /&gt;
    relationshipTag: &amp;quot;has_notificationGroup&amp;quot;,      // name of relationshiptag of objType&lt;br /&gt;
    relationshipServiceTag: &amp;quot;NotificationManager&amp;quot;  // point to service tag that contain data of relationshipTag of objType&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    objectType: &amp;quot;userContact&amp;quot;,                     // objectType in local service&lt;br /&gt;
    relationshipTag: &amp;quot;disabled_notificationGroup&amp;quot;, // name of relationshiptag of objType&lt;br /&gt;
    relationshipServiceTag: &amp;quot;NotificationManager&amp;quot;  // point to service tag that contain data of relationshipTag of objType&lt;br /&gt;
  },&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Creating a new user contact =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of available contact methods&lt;br /&gt;
* User choses one and and is directed to that contact method handler to create the actual entry&lt;br /&gt;
* Once the user contact is created in the handler service a message gets sent to this service which creates it's own UserContacts record&lt;br /&gt;
&lt;br /&gt;
= Creating a new notification group =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of the users contacts&lt;br /&gt;
* How do we handle authentication, notification manager has no record of the userId eg for notificationGroups, only UCM has this&lt;br /&gt;
* (for now not direct to notification manager, any updates go through UCM, but if can figure out some token system could go direct to notification manager) User choses one and is directed to [[Service - Notification Manager|Notification Manager]] service&lt;br /&gt;
* For the endpoint given to Notification Manager try having notifications go directly to the contact method handler. Another option would be to pass back through this service but I cannot see any benefit in this.&lt;br /&gt;
* Notification Manager completes the flow, creating a notification group and associated notification records&lt;br /&gt;
&lt;br /&gt;
= Location specific contact methods =&lt;br /&gt;
&lt;br /&gt;
* Could add ways of restricting the available contact methods to ones the user is likely to want&lt;br /&gt;
* eg: by location (eg country)&lt;br /&gt;
* but have a way that the user can choose any method&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
* If method handler changes the contactName, a message will be sent to User Contact Manager to update its record, contactName is part of the sort key, not sure if it can be updated, if not need to copy the record to a new record with the updated contactName&lt;br /&gt;
* If method handler disables a contact, disable here and send message to [[Service - Notification Manager|Notification Manager]] service which will disable notifications and tell [[Service - Activity Switchboard|Activity Switchboard]] service to remove all associated triggers&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - User Contact Manager|User Contact Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| User Contact Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2690</id>
		<title>Service - Activity Switchboard</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2690"/>
		<updated>2024-08-09T01:48:39Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Receives most messages sent within the entire project's serverless flow, allows services to register a set of triggers that are checked when each message is received, if all triggers pass a message is sent out for subscribed services.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/activity-switchboard/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== TriggerGroups ==&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: comes from: {receiverTag}_{uniqueId}&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service name eg. NotificationMgr&lt;br /&gt;
; uniqueId &lt;br /&gt;
: comes from receiver service, eg: {notificationId}, cannot include underscores&lt;br /&gt;
; triggers&lt;br /&gt;
: array of objects (DynamoDB list?)&lt;br /&gt;
: each element has triggerId, triggerGroupId, valueType, propertyName, propertyValueString or propertyValueNumber&lt;br /&gt;
&lt;br /&gt;
== Triggers ==&lt;br /&gt;
&lt;br /&gt;
; triggerId (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: string&lt;br /&gt;
: create by [[#triggerGroup]]&lt;br /&gt;
; valueType&lt;br /&gt;
: string &lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== triggerGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggers, all triggers for a trigger group must pass for the trigger group to pass&lt;br /&gt;
* no sort key&lt;br /&gt;
* if multiple values are sent for a trigger we consider this an &amp;quot;or&amp;quot; check, any of the values can match. We do this by saving each value as a separate trigger, then when checking a trigger groups child triggers we have a counter that enables a check if any one of the values matches&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;triggerGroup&amp;quot;,&lt;br /&gt;
	canDelete: true,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;TriggerGroups&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		triggers: {&lt;br /&gt;
			type: &amp;quot;array&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		receiverTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		uniqueId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldNames: [&amp;quot;receiverTag&amp;quot;, &amp;quot;uniqueId&amp;quot;],&lt;br /&gt;
			fieldName: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: comes from: {receiverTag}_{uniqueId}&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service name eg. NotificationMgr&lt;br /&gt;
; uniqueId &lt;br /&gt;
: comes from receiver service, eg: {notificationId}, cannot include underscores&lt;br /&gt;
; triggers&lt;br /&gt;
: array of objects (DynamoDB list?)&lt;br /&gt;
: each element has triggerId, triggerGroupId, valueType, propertyName,  propertyValueString and propertyValueNumber&lt;br /&gt;
&lt;br /&gt;
=== trigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;trigger&amp;quot;,&lt;br /&gt;
	canDelete: true,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;Triggers&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;ActivitySwitchboard&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
: attributes is for message attributes&lt;br /&gt;
: property is from the data sent inside the message body&lt;br /&gt;
: hash property name and value so no ambiguity about underscores/spaces etc..&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: (sort key)&lt;br /&gt;
: created by [[#triggerGroup]]&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Efficiency =&lt;br /&gt;
&lt;br /&gt;
* Service will result in a large number of queries to Triggers table, every message will need to make a query for every field set as an activityTrigger&lt;br /&gt;
* Try to make this as efficient as possible&lt;br /&gt;
* To reduce number of queries made to Tiggers table we use the msgCfg for any message received&lt;br /&gt;
** MsgCfg sets which properties can be used as triggers, others are not queried&lt;br /&gt;
&lt;br /&gt;
= Handling msgCfgs =&lt;br /&gt;
&lt;br /&gt;
* msgCfgs get updated from [[Service - Message Config Manager|Message Config Manager]] service, we do this by subscribing to Message Config Manager's OutMsgCfgUpdate topic&lt;br /&gt;
* Only want to process messages from certain In/Out topics, it will be most topics but does not have to be all&lt;br /&gt;
* Set which topics to subscribe to by setting activitySwitchboard = true in msgCfg&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
* If more efficient can use cache for regular DynamoDB queries&lt;br /&gt;
* Could add other matching methods such as greater than or less than, in DynamoDB this might be more efficient to add as separate table with its own structure, eg: partition key is the field reference, sort key is the amount, then use sort key to return matching triggers. Danger of bad partitioning in DynamoDB (or hitting limits) due to large numbers of sort keys associated with one partition key&lt;br /&gt;
* could consider how to incorporate includes or checking within a set of options set in the trigger, again might need specialized table structure&lt;br /&gt;
* Might be able to optimise by using SNS &amp;gt; stream instead of SNS &amp;gt; SQS for all incomming messages&lt;br /&gt;
* One system level receiving service could be specialized logs, eg logs per service per user/per product/etc&lt;br /&gt;
* Topic name is not fixed part of the TriggersGroup structure, allow for trigger groups that are not connected to specific topics, but can pass messages from any topic that matches other triggers&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Activity Switchboard|Working_documents - Activity Switchboard]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Activity Switchboard]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2689</id>
		<title>2020-11-08 - Notification Manager - Functions</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2689"/>
		<updated>2024-08-08T09:20:04Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Lambda Functions =&lt;br /&gt;
&lt;br /&gt;
== RcvActivityMsg ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Receives one activity message from Activity Switchboard, find matching notification, stores for consolidation or sends immediately&lt;br /&gt;
 * @param {Object} activityMsg&lt;br /&gt;
 * @param {Object} activityMsg.messageAttributes&lt;br /&gt;
 * @param {Object} activityMsg.message&lt;br /&gt;
 * @param {String} uniqueId - In NotificationMgr, uniqueId is notificationId&lt;br /&gt;
 * @param {number} time &lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to [[Service - Activity Switchboard]] OutTriggerGroupPassed topic, (? filter policy limit) receiverTag = {this service's izServiceName}&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
# get notification node by getNodeAndRelationship&lt;br /&gt;
#* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#* toNodeVersionedDataLabels: [&amp;quot;notificationGroupSettings&amp;quot;]&lt;br /&gt;
# if not found notification or notificationGroup node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# If have setting and consolidated = true&lt;br /&gt;
## putItem to ConsolidationPending table&lt;br /&gt;
##* notificationGroupId&lt;br /&gt;
##* time: {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
##* activityMsg&lt;br /&gt;
# else &lt;br /&gt;
#* [[#sendNotificationToReceiver]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== DisableNotificationGroups ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} userContactId&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotificationGroups topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;userContact&amp;quot;&lt;br /&gt;
#* nodeProperties.userContactId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else &lt;br /&gt;
## for each notificationGroup&lt;br /&gt;
#* use function [[#disableNotificationGroup]] &lt;br /&gt;
&lt;br /&gt;
== UpdateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Update a notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated &lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue&lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {boolean} enabled&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InUpdateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot; or &amp;quot;disabled_notification&amp;quot; (depend on ''enable'')&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else update notificationGroup node by send message to [[Service - Graph Handler]] InCreateVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties.notificationGroupId&lt;br /&gt;
#* versionDataStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSetting&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# if enabled = false&lt;br /&gt;
## use function [[#disableNotificationGroup]]&lt;br /&gt;
# if enabled = true&lt;br /&gt;
## [[#enableNotificationGroup]]&lt;br /&gt;
&lt;br /&gt;
== DisableNotification ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {string} userId &lt;br /&gt;
 * @param {string} notificationId&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotification topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
# if not found notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
#* receiverTag: process.env.iz_serviceName&lt;br /&gt;
#* notificationId&lt;br /&gt;
# send message to OutNotificationDisabled topic&lt;br /&gt;
#* notificationId&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates one notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue &lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {string} recieverTag&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create notificationGroupId by random UUID&lt;br /&gt;
# if !''consolidate'' set ''consolidate'' is false&lt;br /&gt;
# create ''versionedDataProperties'' object&lt;br /&gt;
#* consolidate&lt;br /&gt;
# if ''consolidated'' = true, validate:&lt;br /&gt;
## consolidatedType must equal &amp;quot;overview&amp;quot; or &amp;quot;detailed&amp;quot;&lt;br /&gt;
## consolidatedSendIfEmpty set default is &amp;quot;false&amp;quot;&lt;br /&gt;
## consolidatedFrequency ..? (not sure, maybe use some sort of standard like cron)&lt;br /&gt;
## if have ''notificationGroupName'' save it to ''versionedDataProperties''&lt;br /&gt;
## ''versionedDataProperties'': {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# create notificationGroup by send message to [[Service - Graph Handler]] InCreateNodeWithVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupId, recieverTag}&lt;br /&gt;
#* versionedDatas&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSettings&amp;quot;&lt;br /&gt;
#** nodeProperties: ''versionedDataProperties''&lt;br /&gt;
# send message to OutNotificationGroupCreated topic, can include full notificationGroup object&lt;br /&gt;
&lt;br /&gt;
== CreateNotification ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates notification&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationName&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotification topic&lt;br /&gt;
    &lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create ''notificationId'' by random UUID &lt;br /&gt;
# if notification have ''notificationName''&lt;br /&gt;
#* send message to [[Service - Graph Handler]] InCreateChildNodeWithVersionedData&lt;br /&gt;
# else&lt;br /&gt;
#* send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates notificationTrigger&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationId&lt;br /&gt;
 * @param {string} propertyName&lt;br /&gt;
 * @param {string} valueType - property|attribute|serviceName|topicName&lt;br /&gt;
 * @param {string} [valueString] - either value or values will be set&lt;br /&gt;
 * @param {number} [valueNumber] - either value or values will be set&lt;br /&gt;
 * @param {string[]} [values] - either value or values will be set&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationTrigger topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create ''notificationTriggerId'' by random UUID&lt;br /&gt;
# [[#notificationTriggerValidator]]&lt;br /&gt;
## if validateResult is &amp;quot;error&amp;quot;&lt;br /&gt;
### send message to OutCreateNotificationTriggerFailed&lt;br /&gt;
###* notificationTriggerId&lt;br /&gt;
###* errorsFound: {errorsFound}&lt;br /&gt;
# send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
&lt;br /&gt;
== NotificationGroup/List ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Lists all notification groups for one primary key (notificationGroupId/uniqueId)&lt;br /&gt;
 * @param {string} receiverTag&lt;br /&gt;
 * @param {string} groupingId&lt;br /&gt;
 * @param {string} [uniqueIdPrefix=null]&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Object[]} array of objects, each object is one notification group&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrApi ===&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* receiverTag&lt;br /&gt;
* groupingId&lt;br /&gt;
* uniqueIdPrefix with default value&lt;br /&gt;
&lt;br /&gt;
==== handler logic ====&lt;br /&gt;
&lt;br /&gt;
(standard)&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
Query NotificationGroups table for all records that match notificationGroupId and if uniqueIdPrefix not null uniqueId ''starts_with'' uniqueIdPrefix, and return.&lt;br /&gt;
&lt;br /&gt;
= Functions =&lt;br /&gt;
&lt;br /&gt;
== sendNotificationToReceiver ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notification - object from the Notifications table&lt;br /&gt;
 * @param {Object} notificationGroup - object from the NotificationGroups table&lt;br /&gt;
 * @param {string} notificationBody - string sent to receiver&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#:return; notificationGroup and userContact node (in future; will have another service eg. SMS, site notification)&lt;br /&gt;
# send message to OutNewNotification topic, adding below properties:&lt;br /&gt;
#* uniqueId&lt;br /&gt;
#* notificationBody&lt;br /&gt;
#* (? filter policy limit) receiverTag gets added to message attributes so can be filtered for by receiving service&lt;br /&gt;
&lt;br /&gt;
;  [[Service - User Contact Manager]] and [[Service - Contact Method Email]] will change logic or structure &lt;br /&gt;
&lt;br /&gt;
== sharedCreateTriggerGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notificationId&lt;br /&gt;
 * @param {Array} notificationTriggers&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# send message to ActySwichboardTestInCreateTriggerGroup topic, adding below properties:&lt;br /&gt;
#* receiverTag: {process.env.iz_serviceName}&lt;br /&gt;
#* uniqueId: {notificationId}&lt;br /&gt;
#* requestedTriggers: notificationTriggers&lt;br /&gt;
&lt;br /&gt;
== notificationTriggerValidator ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate notificationTrigger&lt;br /&gt;
 * @param {Object} notificationTrigger&lt;br /&gt;
 * @param {string} notificationTrigger.propertyName - optional&lt;br /&gt;
 * @param {string} notificationTrigger.valueType - require&lt;br /&gt;
 * @param {string} notificationTrigger.valueString - optional&lt;br /&gt;
 * @param {number} notificationTrigger.valueNumber - optional&lt;br /&gt;
 * @param {Array} notificationTrigger.values - optional&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Array}&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# vatidate notificationTrigger (see. [[Service - Notification Manager]]) collect error and return status&lt;br /&gt;
#* notificationTrigger must have only one of those [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have ''propertyName''&lt;br /&gt;
#* validate type of properties [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#:- typeof &amp;quot;valueString&amp;quot; is ''string''&lt;br /&gt;
#:- typeof &amp;quot;valueNumber&amp;quot; is ''number''&lt;br /&gt;
#:- typeof &amp;quot;values&amp;quot; is ''array''&lt;br /&gt;
# return &lt;br /&gt;
#* validateStatus: {&amp;quot;complete&amp;quot; or &amp;quot;error&amp;quot;}&lt;br /&gt;
#* errorsFound[ ]&lt;br /&gt;
&lt;br /&gt;
== reformNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Reform notificationTriggers for send to InCreateTriggerGroup&lt;br /&gt;
 * @param {Object[]} notificationTriggers&lt;br /&gt;
 * &lt;br /&gt;
 * returns {Object[]}&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationTrigger of notificationTriggers&lt;br /&gt;
## notificationTrigger must have ''nodeLabels'' and ''nodeLabels'' is  &amp;quot;notificationTrigger&amp;quot;&lt;br /&gt;
## notificationTrigger must have ''nodeProperties'' &lt;br /&gt;
## use ''nodeProperties'' for [[#notificationTriggerValidator]]&lt;br /&gt;
## if ''error'';  throw new NoRetryError&lt;br /&gt;
## push ''nodeProperties'' to ''notificationTriggerReformed[ ]''&lt;br /&gt;
# return ''notificationTriggerReformed[ ]''&lt;br /&gt;
&lt;br /&gt;
== disableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} userContactId  - default = null&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# if !''userContactId''&lt;br /&gt;
## get notificationGroup node by GraphHandler getNodeAndRelationships for find userContact node&lt;br /&gt;
##* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationGroupId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
# change relationshipType to &amp;quot;disabled_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupDisabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
== enableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notificationTrigger&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
## change relationshipType to &amp;quot;has_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## use child notificationTriggers for [[#reformNotificationTrigger]] &lt;br /&gt;
## use function sharedCreateTriggerGroup.sharedCreateTriggerGroup&lt;br /&gt;
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup&lt;br /&gt;
###* uniqueId: notificationId&lt;br /&gt;
###* requestedTriggers: [{notificationTriggers}]&lt;br /&gt;
## send message to OutNotificationEnabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# change relationshipType to &amp;quot;has_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupEnabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
= Sample notification object sent to receiver service = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	notificationBody: [&lt;br /&gt;
		{&lt;br /&gt;
			messageAttributes: {&lt;br /&gt;
				// xxx: yyy&lt;br /&gt;
			},&lt;br /&gt;
			message: {&lt;br /&gt;
				// full body of message received&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
	],&lt;br /&gt;
	uniqueId: {emailId}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2020-11-08]]&lt;br /&gt;
[[Category:Working documents - Notification Manager| 2020-11-08]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Contact_Method_Email&amp;diff=2688</id>
		<title>Service - Contact Method Email</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Contact_Method_Email&amp;diff=2688"/>
		<updated>2024-08-08T09:19:28Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Manages email contact methods for users, details are not part of the public transparency followed across the majority of the project in order to protect against emails being scraped.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/contact-method-email/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== EmailAddress==&lt;br /&gt;
&lt;br /&gt;
=== Fields ===&lt;br /&gt;
&lt;br /&gt;
; userId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: string&lt;br /&gt;
; uniqueId&lt;br /&gt;
: (sort key)&lt;br /&gt;
: eg a UUID generated when creating the email record, or sent by [[Service - User Contact Manager]]&lt;br /&gt;
; email&lt;br /&gt;
: string &lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== email ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;email&amp;quot;,&lt;br /&gt;
    extendObjType: { // core object that this object extends&lt;br /&gt;
      objectType: &amp;quot;userContact&amp;quot;,&lt;br /&gt;
      serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
    },&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		contactTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: true,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; contactTag&lt;br /&gt;
: string name created by user to publicly identify this email contact&lt;br /&gt;
&lt;br /&gt;
=== emailAddress ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;emailAddress&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [],         // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 	        // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 		// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;EmailAdress&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
			fromObjectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
		},	&lt;br /&gt;
		uniqueId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
			fromObjectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		email: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;userId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;uniqueId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; userId&lt;br /&gt;
: string&lt;br /&gt;
; uniqueId&lt;br /&gt;
: eg a UUID generated when creating the email record, or sent by [[Service - User Contact Manager]]&lt;br /&gt;
; email&lt;br /&gt;
: string&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
* [[Service - User Contact Manager]] sets the notification group's uniqueID to be {emailUniqueId}_{uuid}, we could split the notificationGroupUniqueId to pull out the emailUniqueId, alternative is we also add the emailID to notification group's additionalData which is what we will do because it is more strongly defined&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Contact Method Email|Contact Method Email]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Contact Method Email]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2687</id>
		<title>Service - Notification Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2687"/>
		<updated>2024-08-08T02:18:13Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/notification-manager/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== PendingConsolidation Table ==&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: type: string&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: type: number&lt;br /&gt;
: {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
; activityMsg&lt;br /&gt;
: type: object&lt;br /&gt;
: object containing ''messageAttributes'' and ''message''&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== notificationGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many notificatons together&lt;br /&gt;
* Simplifies management of similar notifications&lt;br /&gt;
* Allows for consolidation of notifications at notification group level&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationGroup&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationGroupSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationGroupName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{ &lt;br /&gt;
					fieldName: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedType&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedFrequency&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedNextSendDue&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;number&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedSendIfEmpty&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		receiverTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; notificationGroupName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service name&lt;br /&gt;
: eg ContactMethodEmail&lt;br /&gt;
; consolidated&lt;br /&gt;
: true|false&lt;br /&gt;
; consolidatedType&lt;br /&gt;
: overview|detailed&lt;br /&gt;
: ''overview'' sends a count of each type of notification, ''detailed'' lists each activity message&lt;br /&gt;
; consolidatedFrequency&lt;br /&gt;
: not sure, maybe use some sort of standard like cron&lt;br /&gt;
; consolidatedNextSendDue&lt;br /&gt;
: timestamp for next due time to send notification&lt;br /&gt;
; consolidatedSendIfEmpty&lt;br /&gt;
: true|false&lt;br /&gt;
; userId&lt;br /&gt;
: for createdBy relationship&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== notification ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notification&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
	complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
   // overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; notificationName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; userId&lt;br /&gt;
: for createdBy relationship&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== notificationTrigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationTrigger&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationTriggerId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		valueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		values: {&lt;br /&gt;
			type: &amp;quot;arrayMixed&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationTriggerId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationTriggerId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; propertyName&lt;br /&gt;
: can be a nested property, use dot notation&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; valueString&lt;br /&gt;
: type: string&lt;br /&gt;
; valueNumber&lt;br /&gt;
: type: number&lt;br /&gt;
; values&lt;br /&gt;
: type: arrayMixed&lt;br /&gt;
; userId&lt;br /&gt;
: for createdBy relationship&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== consolidated ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;PendingConsolidation&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;NotificationMgr&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		time: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		activityMsg: {&lt;br /&gt;
			type: &amp;quot;object&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;time&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId&lt;br /&gt;
; activityMsg&lt;br /&gt;
: copy of the message delivered from Activity Switchboard&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== has_notificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;has_notificationGroup&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: links userContact to notificationGroup&lt;br /&gt;
&lt;br /&gt;
=== disabled_notificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabled_notificationGroup&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== has_notification ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;has_notification&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== disabled_notification ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabled_notification&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== has_notificationTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;has_notificationTrigger&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationTrigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,	&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== disabled_notificationTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabled_notificationTrigger&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationTrigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= value or values array =&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName &lt;br /&gt;
&lt;br /&gt;
= serviceName and topicName triggers =&lt;br /&gt;
&lt;br /&gt;
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass&lt;br /&gt;
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes&lt;br /&gt;
* Can set serviceName+topicName, then only messages in that topic will pass&lt;br /&gt;
* serviceName and topicName can both be submitted as &amp;quot;values&amp;quot; array, creating multiple trigger groups for each combination&lt;br /&gt;
&lt;br /&gt;
= Consolidated notifications =&lt;br /&gt;
&lt;br /&gt;
== How to trigger processing for time based consolidations == &lt;br /&gt;
&lt;br /&gt;
* ....&lt;br /&gt;
* 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)&lt;br /&gt;
&lt;br /&gt;
= Formating the notification body = &lt;br /&gt;
&lt;br /&gt;
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body&lt;br /&gt;
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs&lt;br /&gt;
&lt;br /&gt;
= Ideas = &lt;br /&gt;
&lt;br /&gt;
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level&lt;br /&gt;
* 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&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Notification Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2686</id>
		<title>Service - Activity Switchboard</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Activity_Switchboard&amp;diff=2686"/>
		<updated>2024-08-08T02:18:06Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Receives most messages sent within the entire project's serverless flow, allows services to register a set of triggers that are checked when each message is received, if all triggers pass a message is sent out for subscribed services.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/activity-switchboard/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== TriggerGroups ==&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: comes from: {receiverTag}_{uniqueId}&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service name eg. NotificationMgr&lt;br /&gt;
; uniqueId &lt;br /&gt;
: comes from receiver service, eg: {notificationId}, cannot include underscores&lt;br /&gt;
; triggers&lt;br /&gt;
: array of objects (DynamoDB list?)&lt;br /&gt;
: each element has triggerId, triggerGroupId, valueType, propertyName, propertyValueString or propertyValueNumber&lt;br /&gt;
&lt;br /&gt;
== Triggers ==&lt;br /&gt;
&lt;br /&gt;
; triggerId (partition key)&lt;br /&gt;
: string&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: string&lt;br /&gt;
: create by [[#triggerGroup]]&lt;br /&gt;
; valueType&lt;br /&gt;
: string &lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== triggerGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many triggers, all triggers for a trigger group must pass for the trigger group to pass&lt;br /&gt;
* no sort key&lt;br /&gt;
* if multiple values are sent for a trigger we consider this an &amp;quot;or&amp;quot; check, any of the values can match. We do this by saving each value as a separate trigger, then when checking a trigger groups child triggers we have a counter that enables a check if any one of the values matches&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;triggerGroup&amp;quot;,&lt;br /&gt;
	canDelete: true,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;TriggerGroups&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		triggers: {&lt;br /&gt;
			type: &amp;quot;array&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		receiverTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		uniqueId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldNames: [&amp;quot;receiverTag&amp;quot;, &amp;quot;uniqueId&amp;quot;],&lt;br /&gt;
			fieldName: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: comes from: {receiverTag}_{uniqueId}&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service name eg. NotificationMgr&lt;br /&gt;
; uniqueId &lt;br /&gt;
: comes from receiver service, eg: {notificationId}, cannot include underscores&lt;br /&gt;
; triggers&lt;br /&gt;
: array of objects (DynamoDB list?)&lt;br /&gt;
: each element has triggerId, triggerGroupId, valueType, propertyName,  propertyValueString and propertyValueNumber&lt;br /&gt;
&lt;br /&gt;
=== trigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;trigger&amp;quot;,&lt;br /&gt;
	canDelete: true,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 	    // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 			// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;, &amp;quot;delete&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;Triggers&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		triggerId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		triggerGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;ActivitySwitchboard&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;triggerGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyValueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;triggerGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; triggerId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: comes from: {hash of valueType(property|attribute).propertyName}_{hash of propertyValue}&lt;br /&gt;
: or: serviceName_{hash of serviceName value}&lt;br /&gt;
: or: topicName_{hash of topicName value}&lt;br /&gt;
: attributes is for message attributes&lt;br /&gt;
: property is from the data sent inside the message body&lt;br /&gt;
: hash property name and value so no ambiguity about underscores/spaces etc..&lt;br /&gt;
; triggerGroupId&lt;br /&gt;
: (sort key)&lt;br /&gt;
: created by [[#triggerGroup]]&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; propertyName&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
: eg. property.propA.propB.propC&lt;br /&gt;
: eg. attribute.serviceName&lt;br /&gt;
; propertyValueString&lt;br /&gt;
: type of this propertyValueString is string from valueString in [[Service - Notification Manager]]&lt;br /&gt;
; propertyValueNumber&lt;br /&gt;
: type of this propertyValueNumber is number from valueNumber in [[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
== RefObjectRelationships ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
// reference to relationshipTag in external service&lt;br /&gt;
[&lt;br /&gt;
  {&lt;br /&gt;
    objectType: &amp;quot;userContact&amp;quot;,                      // objectType in local service&lt;br /&gt;
    relationshipTag: &amp;quot;has_notificationGroup&amp;quot;,       // name of relationshiptag of objType&lt;br /&gt;
    relationshipServiceTag: &amp;quot;NotificationManager&amp;quot;   // point to service tag that contain data of relationshipTag of objType&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    objectType: &amp;quot;userContact&amp;quot;,                      // objectType in local service&lt;br /&gt;
    relationshipTag: &amp;quot;disabled_notificationGroup&amp;quot;,  // name of relationshiptag of objType&lt;br /&gt;
    relationshipServiceTag: &amp;quot;NotificationManager&amp;quot;   // point to service tag that contain data of relationshipTag of objType&lt;br /&gt;
  },&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Efficiency =&lt;br /&gt;
&lt;br /&gt;
* Service will result in a large number of queries to Triggers table, every message will need to make a query for every field set as an activityTrigger&lt;br /&gt;
* Try to make this as efficient as possible&lt;br /&gt;
* To reduce number of queries made to Tiggers table we use the msgCfg for any message received&lt;br /&gt;
** MsgCfg sets which properties can be used as triggers, others are not queried&lt;br /&gt;
&lt;br /&gt;
= Handling msgCfgs =&lt;br /&gt;
&lt;br /&gt;
* msgCfgs get updated from [[Service - Message Config Manager|Message Config Manager]] service, we do this by subscribing to Message Config Manager's OutMsgCfgUpdate topic&lt;br /&gt;
* Only want to process messages from certain In/Out topics, it will be most topics but does not have to be all&lt;br /&gt;
* Set which topics to subscribe to by setting activitySwitchboard = true in msgCfg&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
* If more efficient can use cache for regular DynamoDB queries&lt;br /&gt;
* Could add other matching methods such as greater than or less than, in DynamoDB this might be more efficient to add as separate table with its own structure, eg: partition key is the field reference, sort key is the amount, then use sort key to return matching triggers. Danger of bad partitioning in DynamoDB (or hitting limits) due to large numbers of sort keys associated with one partition key&lt;br /&gt;
* could consider how to incorporate includes or checking within a set of options set in the trigger, again might need specialized table structure&lt;br /&gt;
* Might be able to optimise by using SNS &amp;gt; stream instead of SNS &amp;gt; SQS for all incomming messages&lt;br /&gt;
* One system level receiving service could be specialized logs, eg logs per service per user/per product/etc&lt;br /&gt;
* Topic name is not fixed part of the TriggersGroup structure, allow for trigger groups that are not connected to specific topics, but can pass messages from any topic that matches other triggers&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Activity Switchboard|Working_documents - Activity Switchboard]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Activity Switchboard]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=2685</id>
		<title>Service - User Contact Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_User_Contact_Manager&amp;diff=2685"/>
		<updated>2024-08-07T09:33:25Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Manages contact methods available to users. Records each users contacts.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/user-contact-manager/src/master/&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== userContact ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;userContact&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrApi', 'hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				        // default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			        // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;],&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;userContactSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
					fieldName: &amp;quot;enable&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		userContactId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		methodTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		uniqueId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldNames: [&amp;quot;methodTag&amp;quot;, &amp;quot;uniqueId&amp;quot;],&lt;br /&gt;
			name: &amp;quot;userContactId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== fieldNames ===&lt;br /&gt;
&lt;br /&gt;
; userContactId&lt;br /&gt;
: comes from {methodTag}_{methods uniqueId, eg: emailUniqueId}&lt;br /&gt;
; methodTag&lt;br /&gt;
: comes from {serviceName} of receiver service &lt;br /&gt;
; uniqueId&lt;br /&gt;
: random UUID from receiver service&lt;br /&gt;
; userId&lt;br /&gt;
: id of user that create userContact &lt;br /&gt;
; enabled&lt;br /&gt;
: true|false&lt;br /&gt;
&lt;br /&gt;
= Creating a new user contact =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of available contact methods&lt;br /&gt;
* User choses one and and is directed to that contact method handler to create the actual entry&lt;br /&gt;
* Once the user contact is created in the handler service a message gets sent to this service which creates it's own UserContacts record&lt;br /&gt;
&lt;br /&gt;
= Creating a new notification group =&lt;br /&gt;
&lt;br /&gt;
* This service presents a list of the users contacts&lt;br /&gt;
* How do we handle authentication, notification manager has no record of the userId eg for notificationGroups, only UCM has this&lt;br /&gt;
* (for now not direct to notification manager, any updates go through UCM, but if can figure out some token system could go direct to notification manager) User choses one and is directed to [[Service - Notification Manager|Notification Manager]] service&lt;br /&gt;
* For the endpoint given to Notification Manager try having notifications go directly to the contact method handler. Another option would be to pass back through this service but I cannot see any benefit in this.&lt;br /&gt;
* Notification Manager completes the flow, creating a notification group and associated notification records&lt;br /&gt;
&lt;br /&gt;
= Location specific contact methods =&lt;br /&gt;
&lt;br /&gt;
* Could add ways of restricting the available contact methods to ones the user is likely to want&lt;br /&gt;
* eg: by location (eg country)&lt;br /&gt;
* but have a way that the user can choose any method&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
* If method handler changes the contactName, a message will be sent to User Contact Manager to update its record, contactName is part of the sort key, not sure if it can be updated, if not need to copy the record to a new record with the updated contactName&lt;br /&gt;
* If method handler disables a contact, disable here and send message to [[Service - Notification Manager|Notification Manager]] service which will disable notifications and tell [[Service - Activity Switchboard|Activity Switchboard]] service to remove all associated triggers&lt;br /&gt;
&lt;br /&gt;
= Ideas =&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - User Contact Manager|User Contact Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| User Contact Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Contact_Method_Email&amp;diff=2684</id>
		<title>Service - Contact Method Email</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Contact_Method_Email&amp;diff=2684"/>
		<updated>2024-08-07T09:32:10Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
Manages email contact methods for users, details are not part of the public transparency followed across the majority of the project in order to protect against emails being scraped.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/contact-method-email/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== EmailAddress==&lt;br /&gt;
&lt;br /&gt;
=== Fields ===&lt;br /&gt;
&lt;br /&gt;
; userId&lt;br /&gt;
: (partition key)&lt;br /&gt;
: string&lt;br /&gt;
; uniqueId&lt;br /&gt;
: (sort key)&lt;br /&gt;
: eg a UUID generated when creating the email record, or sent by [[Service - User Contact Manager]]&lt;br /&gt;
; email&lt;br /&gt;
: string &lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== email ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;email&amp;quot;,&lt;br /&gt;
    extendObjType: { // core object that this object extends&lt;br /&gt;
      objectType: &amp;quot;userContact&amp;quot;,&lt;br /&gt;
      serviceTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
    },&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		contactTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: true,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; contactTag&lt;br /&gt;
: string name created by user to publicly identify this email contact&lt;br /&gt;
&lt;br /&gt;
=== emailAddress ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;emailAddress&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [],         // default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 	// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 		// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;EmailAdress&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
			fromObjectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
		},	&lt;br /&gt;
		uniqueId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;UserContactManager&amp;quot;,&lt;br /&gt;
			fromObjectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		email: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;userId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;uniqueId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; userId&lt;br /&gt;
: string&lt;br /&gt;
; uniqueId&lt;br /&gt;
: eg a UUID generated when creating the email record, or sent by [[Service - User Contact Manager]]&lt;br /&gt;
; email&lt;br /&gt;
: string&lt;br /&gt;
&lt;br /&gt;
= Notes =&lt;br /&gt;
&lt;br /&gt;
* [[Service - User Contact Manager]] sets the notification group's uniqueID to be {emailUniqueId}_{uuid}, we could split the notificationGroupUniqueId to pull out the emailUniqueId, alternative is we also add the emailID to notification group's additionalData which is what we will do because it is more strongly defined&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working_documents - Contact Method Email|Contact Method Email]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Contact Method Email]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2678</id>
		<title>2020-11-08 - Notification Manager - Functions</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2678"/>
		<updated>2024-08-06T01:28:39Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Lambda Functions =&lt;br /&gt;
&lt;br /&gt;
== RcvActivityMsg ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Receives one activity message from Activity Switchboard, find matching notification, stores for consolidation or sends immediately&lt;br /&gt;
 * @param {Object[]} event.activityMsg&lt;br /&gt;
 * @param {Object} event.activityMsg.messageAttributes&lt;br /&gt;
 * @param {Object} event.activityMsg.message&lt;br /&gt;
 * @param {String} event.uniqueId - In NotificationMgr, uniqueId is notificationId&lt;br /&gt;
 * @param {number} event.time &lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to [[Service - Activity Switchboard]] OutTriggerGroupPassed topic, (? filter policy limit) receiverTag = {this service's izServiceName}&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
# get notification node by getNodeAndRelationship&lt;br /&gt;
#* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#* toNodeVersionedDataLabels: [&amp;quot;notificationGroupSettings&amp;quot;]&lt;br /&gt;
# if not found notification or notificationGroup node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# If have setting and consolidated = true&lt;br /&gt;
## putItem to ConsolidationPending table&lt;br /&gt;
##* notificationGroupId&lt;br /&gt;
##* time: {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
##* activityMsg&lt;br /&gt;
# else &lt;br /&gt;
#* [[#sendNotificationToReceiver]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== DisableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId - null if not supplied&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else &lt;br /&gt;
#* use function [[#disableNotificationGroup]] &lt;br /&gt;
&lt;br /&gt;
== UpdateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Update a notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue&lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {boolean} enabled&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InUpdateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot; or &amp;quot;disabled_notification&amp;quot; (depend on ''enable'')&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else update notificationGroup node by send message to [[Service - Graph Handler]] InCreateVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties.notificationGroupId&lt;br /&gt;
#* versionDataStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSetting&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# if enabled = false&lt;br /&gt;
## use function [[#disableNotificationGroup]]&lt;br /&gt;
# if enabled = true&lt;br /&gt;
## [[#enableNotificationGroup]]&lt;br /&gt;
&lt;br /&gt;
== DisableNotification ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {string} userId &lt;br /&gt;
 * @param {string} notificationId&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotification topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
## if not found notification node&lt;br /&gt;
##* throw new NoRetryError &lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag: process.env.iz_serviceName&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates one notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue &lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {string} recieverTag&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create notificationGroupId by random UUID&lt;br /&gt;
# if ''consolidated'' = true, validate:&lt;br /&gt;
## consolidatedType must equal &amp;quot;overview&amp;quot; or &amp;quot;detailed&amp;quot;&lt;br /&gt;
## consolidatedSendIfEmpty set default is &amp;quot;false&amp;quot;&lt;br /&gt;
## consolidatedFrequency ..? (not sure, maybe use some sort of standard like cron)&lt;br /&gt;
# create notificationGroup by send message to [[Service - Graph Handler]] InCreateNodeWithVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupId, recieverTag}&lt;br /&gt;
#* versionedDatas&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSettings&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# send message to OutNotificationGroupCreated topic, can include full notificationGroup object&lt;br /&gt;
&lt;br /&gt;
== CreateNotification ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates notification&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationName&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotification topic&lt;br /&gt;
    &lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create ''notificationId'' by random UUID &lt;br /&gt;
# if notification have ''notificationName''&lt;br /&gt;
#* send message to [[Service - Graph Handler]] InCreateChildNodeWithVersionedData&lt;br /&gt;
# else&lt;br /&gt;
#* send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates notificationTrigger&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationId&lt;br /&gt;
 * @param {string} propertyName&lt;br /&gt;
 * @param {string} valueType - property|attribute|serviceName|topicName&lt;br /&gt;
 * @param {string} [valueString] - either value or values will be set&lt;br /&gt;
 * @param {number} [valueNumber] - either value or values will be set&lt;br /&gt;
 * @param {string[]} [values] - either value or values will be set&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationTrigger topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create ''notificationTriggerId'' by random UUID&lt;br /&gt;
# [[#notificationTriggerValidator]]&lt;br /&gt;
## if validateResult is &amp;quot;error&amp;quot;&lt;br /&gt;
### send message to OutCreateNotificationTriggerFailed&lt;br /&gt;
###* notificationTriggerId&lt;br /&gt;
###* errorsFound: {errorsFound}&lt;br /&gt;
# send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
&lt;br /&gt;
== NotificationGroup/List ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Lists all notification groups for one primary key (notificationGroupId/uniqueId)&lt;br /&gt;
 * @param {string} receiverTag&lt;br /&gt;
 * @param {string} groupingId&lt;br /&gt;
 * @param {string} [uniqueIdPrefix=null]&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Object[]} array of objects, each object is one notification group&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrApi ===&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* receiverTag&lt;br /&gt;
* groupingId&lt;br /&gt;
* uniqueIdPrefix with default value&lt;br /&gt;
&lt;br /&gt;
==== handler logic ====&lt;br /&gt;
&lt;br /&gt;
(standard)&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
Query NotificationGroups table for all records that match notificationGroupId and if uniqueIdPrefix not null uniqueId ''starts_with'' uniqueIdPrefix, and return.&lt;br /&gt;
&lt;br /&gt;
= Functions =&lt;br /&gt;
&lt;br /&gt;
== sendNotificationToReceiver ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notification - object from the Notifications table&lt;br /&gt;
 * @param {Object} notificationGroup - object from the NotificationGroups table&lt;br /&gt;
 * @param {string} notificationBody - string sent to receiver&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#:return; notificationGroup and userContact node (in future; will have another service eg. SMS, site notification)&lt;br /&gt;
# send message to OutNewNotification topic, adding below properties:&lt;br /&gt;
#* uniqueId&lt;br /&gt;
#* notificationBody&lt;br /&gt;
#* (? filter policy limit) receiverTag gets added to message attributes so can be filtered for by receiving service&lt;br /&gt;
&lt;br /&gt;
;  [[Service - User Contact Manager]] and [[Service - Contact Method Email]] will change logic or structure &lt;br /&gt;
&lt;br /&gt;
== sharedCreateTriggerGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notificationId&lt;br /&gt;
 * @param {Array} notificationTriggers&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# send message to ActySwichboardTestInCreateTriggerGroup topic, adding below properties:&lt;br /&gt;
#* receiverTag: {process.env.iz_serviceName}&lt;br /&gt;
#* uniqueId: {notificationId}&lt;br /&gt;
#* requestedTriggers: notificationTriggers&lt;br /&gt;
&lt;br /&gt;
== notificationTriggerValidator ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate notificationTrigger&lt;br /&gt;
 * @param {Object} notificationTrigger&lt;br /&gt;
 * @param {string} notificationTrigger.propertyName - optional&lt;br /&gt;
 * @param {string} notificationTrigger.valueType - require&lt;br /&gt;
 * @param {string} notificationTrigger.valueString - optional&lt;br /&gt;
 * @param {number} notificationTrigger.valueNumber - optional&lt;br /&gt;
 * @param {Array} notificationTrigger.values - optional&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Array}&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# vatidate notificationTrigger (see. [[Service - Notification Manager]]) collect error and return status&lt;br /&gt;
#* notificationTrigger must have only one of those [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have ''propertyName''&lt;br /&gt;
#* validate type of properties [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#:- typeof &amp;quot;valueString&amp;quot; is ''string''&lt;br /&gt;
#:- typeof &amp;quot;valueNumber&amp;quot; is ''number''&lt;br /&gt;
#:- typeof &amp;quot;values&amp;quot; is ''array''&lt;br /&gt;
# return &lt;br /&gt;
#* validateStatus: {&amp;quot;complete&amp;quot; or &amp;quot;error&amp;quot;}&lt;br /&gt;
#* errorsFound[ ]&lt;br /&gt;
&lt;br /&gt;
== reformNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Reform notificationTriggers for send to InCreateTriggerGroup&lt;br /&gt;
 * @param {Object[]} notificationTriggers&lt;br /&gt;
 * &lt;br /&gt;
 * returns {Object[]}&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationTrigger of notificationTriggers&lt;br /&gt;
## notificationTrigger must have ''nodeLabels'' and ''nodeLabels'' is  &amp;quot;notificationTrigger&amp;quot;&lt;br /&gt;
## notificationTrigger must have ''nodeProperties'' &lt;br /&gt;
## use ''nodeProperties'' for [[#notificationTriggerValidator]]&lt;br /&gt;
## if ''error'';  throw new NoRetryError&lt;br /&gt;
## push ''nodeProperties'' to ''notificationTriggerReformed[ ]''&lt;br /&gt;
# return ''notificationTriggerReformed[ ]''&lt;br /&gt;
&lt;br /&gt;
== disableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships for find userContact&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
# change relationshipType to &amp;quot;disabled_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupDisabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
== enableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notificationTrigger&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
## change relationshipType to &amp;quot;has_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## use child notificationTriggers for [[#reformNotificationTrigger]] &lt;br /&gt;
## use function sharedCreateTriggerGroup.sharedCreateTriggerGroup&lt;br /&gt;
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup&lt;br /&gt;
###* uniqueId: notificationId&lt;br /&gt;
###* requestedTriggers: [{notificationTriggers}]&lt;br /&gt;
## send message to OutNotificationEnabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# change relationshipType to &amp;quot;has_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupEnabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
= Sample notification object sent to receiver service = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	notificationBody: [&lt;br /&gt;
		{&lt;br /&gt;
			messageAttributes: {&lt;br /&gt;
				// xxx: yyy&lt;br /&gt;
			},&lt;br /&gt;
			message: {&lt;br /&gt;
				// full body of message received&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
	],&lt;br /&gt;
	uniqueId: {emailId}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2020-11-08]]&lt;br /&gt;
[[Category:Working documents - Notification Manager| 2020-11-08]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2676</id>
		<title>2020-11-08 - Notification Manager - Functions</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2676"/>
		<updated>2024-08-05T09:11:27Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Lambda Functions =&lt;br /&gt;
&lt;br /&gt;
== RcvActivityMsg ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Receives one activity message from Activity Switchboard, find matching notification, stores for consolidation or sends immediately&lt;br /&gt;
 * @param {Object[]} event.activityMsg&lt;br /&gt;
 * @param {Object} event.activityMsg.messageAttributes&lt;br /&gt;
 * @param {Object} event.activityMsg.message&lt;br /&gt;
 * @param {String} event.uniqueId - In NotificationMgr, uniqueId is notificationId&lt;br /&gt;
 * @param {number} event.time &lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to [[Service - Activity Switchboard]] OutTriggerGroupPassed topic, (? filter policy limit) receiverTag = {this service's izServiceName}&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
# get notification node by getNodeAndRelationship&lt;br /&gt;
#* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#* toNodeVersionedDataLabels: [&amp;quot;notificationGroupSettings&amp;quot;]&lt;br /&gt;
# if not found notification or notificationGroup node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# If have setting and consolidated = true&lt;br /&gt;
## putItem to ConsolidationPending table&lt;br /&gt;
##* notificationGroupId&lt;br /&gt;
##* time: {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
##* activityMsg&lt;br /&gt;
# else &lt;br /&gt;
#* [[#sendNotificationToReceiver]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== DisableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId - null if not supplied&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else &lt;br /&gt;
#* use function [[#disableNotificationGroup]] &lt;br /&gt;
&lt;br /&gt;
== UpdateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Update a notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue&lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {boolean} enabled&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InUpdateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot; or &amp;quot;disabled_notification&amp;quot; (depend on ''enable'')&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else update notificationGroup node by send message to [[Service - Graph Handler]] InCreateVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties.notificationGroupId&lt;br /&gt;
#* versionDataStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSetting&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# if enabled = false&lt;br /&gt;
## use function [[#disableNotificationGroup]]&lt;br /&gt;
# if enabled = true&lt;br /&gt;
## [[#enableNotificationGroup]]&lt;br /&gt;
&lt;br /&gt;
== DisableNotification ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {string} userId &lt;br /&gt;
 * @param {string} notificationId&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotification topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
## if not found notification node&lt;br /&gt;
##* throw new NoRetryError &lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag: process.env.iz_serviceName&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates one notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue &lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {string} recieverTag&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create notificationGroupId by random UUID&lt;br /&gt;
# if ''consolidated'' = true, validate:&lt;br /&gt;
## consolidatedType must equal &amp;quot;overview&amp;quot; or &amp;quot;detailed&amp;quot;&lt;br /&gt;
## consolidatedSendIfEmpty set default is &amp;quot;false&amp;quot;&lt;br /&gt;
## consolidatedFrequency ..? (not sure, maybe use some sort of standard like cron)&lt;br /&gt;
# create notificationGroup by send message to [[Service - Graph Handler]] InCreateNodeWithVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupId, recieverTag}&lt;br /&gt;
#* versionedDatas&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSettings&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# send message to OutNotificationGroupCreated topic, can include full notificationGroup object&lt;br /&gt;
&lt;br /&gt;
== CreateNotification ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates notification&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationName&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotification topic&lt;br /&gt;
    &lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create ''notificationId'' by random UUID &lt;br /&gt;
# if notification have ''notificationName''&lt;br /&gt;
#* send message to [[Service - Graph Handler]] InCreateNodeWithVersionedData&lt;br /&gt;
# else&lt;br /&gt;
#* send message to [[Service - Graph Handler]] InCreateNode&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates notificationTrigger&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} propertyName&lt;br /&gt;
 * @param {string} valueType - property|attribute|serviceName|topicName&lt;br /&gt;
 * @param {string} [valueString] - either value or values will be set&lt;br /&gt;
 * @param {number} [valueNumber] - either value or values will be set&lt;br /&gt;
 * @param {string[]} [values] - either value or values will be set&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationTrigger topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create ''notificationTriggerId'' by random UUID&lt;br /&gt;
# [[#notificationTriggerValidator]]&lt;br /&gt;
## if validateResult is &amp;quot;error&amp;quot;&lt;br /&gt;
### send message to OutCreateNotificationTriggerFailed&lt;br /&gt;
###* notificationTriggerId&lt;br /&gt;
###* errorsFound: {errorsFound}&lt;br /&gt;
# send message to [[Service - Graph Handler]] InCreateNode&lt;br /&gt;
&lt;br /&gt;
== NotificationGroup/List ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Lists all notification groups for one primary key (notificationGroupId/uniqueId)&lt;br /&gt;
 * @param {string} receiverTag&lt;br /&gt;
 * @param {string} groupingId&lt;br /&gt;
 * @param {string} [uniqueIdPrefix=null]&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Object[]} array of objects, each object is one notification group&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrApi ===&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* receiverTag&lt;br /&gt;
* groupingId&lt;br /&gt;
* uniqueIdPrefix with default value&lt;br /&gt;
&lt;br /&gt;
==== handler logic ====&lt;br /&gt;
&lt;br /&gt;
(standard)&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
Query NotificationGroups table for all records that match notificationGroupId and if uniqueIdPrefix not null uniqueId ''starts_with'' uniqueIdPrefix, and return.&lt;br /&gt;
&lt;br /&gt;
= Functions =&lt;br /&gt;
&lt;br /&gt;
== sendNotificationToReceiver ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notification - object from the Notifications table&lt;br /&gt;
 * @param {Object} notificationGroup - object from the NotificationGroups table&lt;br /&gt;
 * @param {string} notificationBody - string sent to receiver&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#:return; notificationGroup and email node (in future; will have another service eg. SMS, site notification)&lt;br /&gt;
# send message to OutNewNotification topic, adding below properties:&lt;br /&gt;
#* uniqueId: {emailId}&lt;br /&gt;
#* notificationBody&lt;br /&gt;
#* (? filter policy limit) receiverTag gets added to message attributes so can be filtered for by receiving service&lt;br /&gt;
&lt;br /&gt;
;  [[Service - User Contact Manager]] and [[Service - Contact Method Email]] will change logic or structure &lt;br /&gt;
&lt;br /&gt;
== sharedCreateTriggerGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notificationId&lt;br /&gt;
 * @param {Array} notificationTriggers&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# send message to ActySwichboardTestInCreateTriggerGroup topic, adding below properties:&lt;br /&gt;
#* receiverTag: {process.env.iz_serviceName}&lt;br /&gt;
#* uniqueId: {notificationId}&lt;br /&gt;
#* requestedTriggers: notificationTriggers&lt;br /&gt;
&lt;br /&gt;
== notificationTriggerValidator ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate notificationTrigger&lt;br /&gt;
 * @param {Object} notificationTrigger&lt;br /&gt;
 * @param {string} notificationTrigger.propertyName - optional&lt;br /&gt;
 * @param {string} notificationTrigger.valueType - require&lt;br /&gt;
 * @param {string} notificationTrigger.valueString - optional&lt;br /&gt;
 * @param {number} notificationTrigger.valueNumber - optional&lt;br /&gt;
 * @param {Array} notificationTrigger.values - optional&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Array}&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# vatidate notificationTrigger (see. [[Service - Notification Manager]]) collect error and return status&lt;br /&gt;
#* notificationTrigger must have only one of those [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have ''propertyName''&lt;br /&gt;
#* validate type of properties [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#:- typeof &amp;quot;valueString&amp;quot; is ''string''&lt;br /&gt;
#:- typeof &amp;quot;valueNumber&amp;quot; is ''number''&lt;br /&gt;
#:- typeof &amp;quot;values&amp;quot; is ''array''&lt;br /&gt;
# return &lt;br /&gt;
#* validateStatus: {&amp;quot;complete&amp;quot; or &amp;quot;error&amp;quot;}&lt;br /&gt;
#* errorsFound[ ]&lt;br /&gt;
&lt;br /&gt;
== reformNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Reform notificationTriggers for send to InCreateTriggerGroup&lt;br /&gt;
 * @param {Object[]} notificationTriggers&lt;br /&gt;
 * &lt;br /&gt;
 * returns {Object[]}&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationTrigger of notificationTriggers&lt;br /&gt;
## notificationTrigger must have ''nodeLabels'' and ''nodeLabels'' is  &amp;quot;notificationTrigger&amp;quot;&lt;br /&gt;
## notificationTrigger must have ''nodeProperties'' &lt;br /&gt;
## use ''nodeProperties'' for [[#notificationTriggerValidator]]&lt;br /&gt;
## if ''error'';  throw new NoRetryError&lt;br /&gt;
## push ''nodeProperties'' to ''notificationTriggerReformed[ ]''&lt;br /&gt;
# return ''notificationTriggerReformed[ ]''&lt;br /&gt;
&lt;br /&gt;
== disableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships for find email&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
# change relationshipType to &amp;quot;disabled_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupDisabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
== enableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notificationTrigger&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
## change relationshipType to &amp;quot;has_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## use child notificationTriggers for [[#reformNotificationTrigger]] &lt;br /&gt;
## use function sharedCreateTriggerGroup.sharedCreateTriggerGroup&lt;br /&gt;
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup&lt;br /&gt;
###* uniqueId: notificationId&lt;br /&gt;
###* requestedTriggers: [{notificationTriggers}]&lt;br /&gt;
## send message to OutNotificationEnabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# change relationshipType to &amp;quot;has_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupEnabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
= Sample notification object sent to receiver service = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	notificationBody: [&lt;br /&gt;
		{&lt;br /&gt;
			messageAttributes: {&lt;br /&gt;
				// xxx: yyy&lt;br /&gt;
			},&lt;br /&gt;
			message: {&lt;br /&gt;
				// full body of message received&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
	],&lt;br /&gt;
	uniqueId: {emailId}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2020-11-08]]&lt;br /&gt;
[[Category:Working documents - Notification Manager| 2020-11-08]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2675</id>
		<title>Service - Notification Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2675"/>
		<updated>2024-08-05T09:08:54Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/notification-manager/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== PendingConsolidation Table ==&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: type: string&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: type: number&lt;br /&gt;
: {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
; activityMsg&lt;br /&gt;
: type: object&lt;br /&gt;
: object containing ''messageAttributes'' and ''message''&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== notificationGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many notificatons together&lt;br /&gt;
* Simplifies management of similar notifications&lt;br /&gt;
* Allows for consolidation of notifications at notification group level&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationGroup&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationGroupSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationGroupName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{ &lt;br /&gt;
					fieldName: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					requiredOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedType&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedFrequency&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedNextSendDue&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;number&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedSendIfEmpty&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		receiverTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; notificationGroupName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service&lt;br /&gt;
: eg ContactMethodEmail&lt;br /&gt;
; consolidated&lt;br /&gt;
: true|false&lt;br /&gt;
; consolidatedType&lt;br /&gt;
: overview|detailed&lt;br /&gt;
: ''overview'' sends a count of each type of notification, ''detailed'' lists each activity message&lt;br /&gt;
; consolidatedFrequency&lt;br /&gt;
: not sure, maybe use some sort of standard like cron&lt;br /&gt;
; consolidatedNextSendDue&lt;br /&gt;
: timestamp for next due time to send notification&lt;br /&gt;
; consolidatedSendIfEmpty&lt;br /&gt;
: true|false&lt;br /&gt;
; userId&lt;br /&gt;
: for createdBy relationship&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== notification ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notification&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
	complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
   // overwriteGeneratedMainFunction: [&amp;quot;update&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; notificationName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; userId&lt;br /&gt;
: for createdBy relationship&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== notificationTrigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationTrigger&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overWriteHandlers: { // optional, if not set will create default handlers, if empty will not create handler and main function&lt;br /&gt;
		create: ['hdrSqs'], 	// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		update: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
		get: [], 				// default: ['hdrApi', 'hdrInv']&lt;br /&gt;
		delete: [], 			// default: ['hdrApi', 'hdrSqs']&lt;br /&gt;
	},&lt;br /&gt;
    // overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationTriggerId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		valueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		values: {&lt;br /&gt;
			type: &amp;quot;arrayMixed&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationTriggerId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationTriggerId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; propertyName&lt;br /&gt;
: can be a nested property, use dot notation&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; valueString&lt;br /&gt;
: type: string&lt;br /&gt;
; valueNumber&lt;br /&gt;
: type: number&lt;br /&gt;
; values&lt;br /&gt;
: type: arrayMixed&lt;br /&gt;
; userId&lt;br /&gt;
: for createdBy relationship&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== consolidated ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;PendingConsolidation&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;NotificationMgr&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		time: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		activityMsg: {&lt;br /&gt;
			type: &amp;quot;object&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;time&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId&lt;br /&gt;
; activityMsg&lt;br /&gt;
: copy of the message delivered from Activity Switchboard&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== has_notificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;has_notificationGroup&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: links userContact to notificationGroup&lt;br /&gt;
&lt;br /&gt;
=== disabled_notificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabled_notificationGroup&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== has_notification ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;has_notification&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== disabled_notification ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabled_notification&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== has_notificationTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;has_notificationTrigger&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationTrigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,	&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== disabled_notificationTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabled_notificationTrigger&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationManager&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationTrigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= value or values array =&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName &lt;br /&gt;
&lt;br /&gt;
= serviceName and topicName triggers =&lt;br /&gt;
&lt;br /&gt;
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass&lt;br /&gt;
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes&lt;br /&gt;
* Can set serviceName+topicName, then only messages in that topic will pass&lt;br /&gt;
* serviceName and topicName can both be submitted as &amp;quot;values&amp;quot; array, creating multiple trigger groups for each combination&lt;br /&gt;
&lt;br /&gt;
= Consolidated notifications =&lt;br /&gt;
&lt;br /&gt;
== How to trigger processing for time based consolidations == &lt;br /&gt;
&lt;br /&gt;
* ....&lt;br /&gt;
* 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)&lt;br /&gt;
&lt;br /&gt;
= Formating the notification body = &lt;br /&gt;
&lt;br /&gt;
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body&lt;br /&gt;
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs&lt;br /&gt;
&lt;br /&gt;
= Ideas = &lt;br /&gt;
&lt;br /&gt;
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level&lt;br /&gt;
* 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&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Notification Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2674</id>
		<title>2020-11-08 - Notification Manager - Functions</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2674"/>
		<updated>2024-08-05T06:52:22Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Lambda Functions =&lt;br /&gt;
&lt;br /&gt;
== RcvActivityMsg ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Receives one activity message from Activity Switchboard, find matching notification, stores for consolidation or sends immediately&lt;br /&gt;
 * @param {Object[]} event.activityMsg&lt;br /&gt;
 * @param {Object} event.activityMsg.messageAttributes&lt;br /&gt;
 * @param {Object} event.activityMsg.message&lt;br /&gt;
 * @param {String} event.uniqueId - In NotificationMgr, uniqueId is notificationId&lt;br /&gt;
 * @param {number} event.time &lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to [[Service - Activity Switchboard]] OutTriggerGroupPassed topic, (? filter policy limit) receiverTag = {this service's izServiceName}&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
# get notification node by getNodeAndRelationship&lt;br /&gt;
#* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#* toNodeVersionedDataLabels: [&amp;quot;notificationGroupSettings&amp;quot;]&lt;br /&gt;
# if not found notification or notificationGroup node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# If have setting and consolidated = true&lt;br /&gt;
#* save ''activityMsg'' to ConsolidationPending table&lt;br /&gt;
# else &lt;br /&gt;
#* [[#sendNotificationToReceiver]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== DisableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId - null if not supplied&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else &lt;br /&gt;
#* use function [[#disableNotificationGroup]] &lt;br /&gt;
&lt;br /&gt;
== UpdateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Update a notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue&lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {boolean} enabled&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InUpdateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot; or &amp;quot;disabled_notification&amp;quot; (depend on ''enable'')&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else update notificationGroup node by send message to [[Service - Graph Handler]] InCreateVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties.notificationGroupId&lt;br /&gt;
#* versionDataStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSetting&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# if enabled = false&lt;br /&gt;
## use function [[#disableNotificationGroup]]&lt;br /&gt;
# if enabled = true&lt;br /&gt;
## [[#enableNotificationGroup]]&lt;br /&gt;
&lt;br /&gt;
== DisableNotification ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {string} userId &lt;br /&gt;
 * @param {string} notificationId&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotification topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
## if not found notification node&lt;br /&gt;
##* throw new NoRetryError &lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag: process.env.iz_serviceName&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates one notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue &lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {string} recieverTag&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create notificationGroupId by random UUID&lt;br /&gt;
# if ''consolidated'' = true, validate:&lt;br /&gt;
## consolidatedType must equal &amp;quot;overview&amp;quot; or &amp;quot;detailed&amp;quot;&lt;br /&gt;
## consolidatedSendIfEmpty set default is &amp;quot;false&amp;quot;&lt;br /&gt;
## consolidatedFrequency ..? (not sure, maybe use some sort of standard like cron)&lt;br /&gt;
# create notificationGroup by send message to [[Service - Graph Handler]] InCreateNodeWithVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupId, recieverTag}&lt;br /&gt;
#* versionedDatas&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSettings&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# send message to OutNotificationGroupCreated topic, can include full notificationGroup object&lt;br /&gt;
&lt;br /&gt;
== CreateNotification ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates notification&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationName&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotification topic&lt;br /&gt;
    &lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create ''notificationId'' by random UUID &lt;br /&gt;
# if notification have ''notificationName''&lt;br /&gt;
#* send message to [[Service - Graph Handler]] InCreateChildNodeWithVersionedData&lt;br /&gt;
# else&lt;br /&gt;
#* send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates notificationTrigger&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} propertyName&lt;br /&gt;
 * @param {string} valueType - property|attribute|serviceName|topicName&lt;br /&gt;
 * @param {string} [valueString] - either value or values will be set&lt;br /&gt;
 * @param {number} [valueNumber] - either value or values will be set&lt;br /&gt;
 * @param {string[]} [values] - either value or values will be set&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationTrigger topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create ''notificationTriggerId'' by random UUID&lt;br /&gt;
# [[#notificationTriggerValidator]]&lt;br /&gt;
## if validateResult is &amp;quot;error&amp;quot;&lt;br /&gt;
### send message to OutCreateNotificationTriggerFailed&lt;br /&gt;
###* notificationTriggerId&lt;br /&gt;
###* errorsFound: {errorsFound}&lt;br /&gt;
# send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
&lt;br /&gt;
== NotificationGroup/List ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Lists all notification groups for one primary key (notificationGroupId/uniqueId)&lt;br /&gt;
 * @param {string} receiverTag&lt;br /&gt;
 * @param {string} groupingId&lt;br /&gt;
 * @param {string} [uniqueIdPrefix=null]&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Object[]} array of objects, each object is one notification group&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrApi ===&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* receiverTag&lt;br /&gt;
* groupingId&lt;br /&gt;
* uniqueIdPrefix with default value&lt;br /&gt;
&lt;br /&gt;
==== handler logic ====&lt;br /&gt;
&lt;br /&gt;
(standard)&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
Query NotificationGroups table for all records that match notificationGroupId and if uniqueIdPrefix not null uniqueId ''starts_with'' uniqueIdPrefix, and return.&lt;br /&gt;
&lt;br /&gt;
= Functions =&lt;br /&gt;
&lt;br /&gt;
== sendNotificationToReceiver ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notification - object from the Notifications table&lt;br /&gt;
 * @param {Object} notificationGroup - object from the NotificationGroups table&lt;br /&gt;
 * @param {string} notificationBody - string sent to receiver&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#:return; notificationGroup and email node (in future; will have another service eg. SMS, site notification)&lt;br /&gt;
# send message to OutNewNotification topic, adding below properties:&lt;br /&gt;
#* uniqueId: {emailId}&lt;br /&gt;
#* notificationBody&lt;br /&gt;
#* (? filter policy limit) receiverTag gets added to message attributes so can be filtered for by receiving service&lt;br /&gt;
&lt;br /&gt;
;  [[Service - User Contact Manager]] and [[Service - Contact Method Email]] will change logic or structure &lt;br /&gt;
&lt;br /&gt;
== sharedCreateTriggerGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notificationId&lt;br /&gt;
 * @param {Array} notificationTriggers&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# send message to ActySwichboardTestInCreateTriggerGroup topic, adding below properties:&lt;br /&gt;
#* receiverTag: {process.env.iz_serviceName}&lt;br /&gt;
#* uniqueId: {notificationId}&lt;br /&gt;
#* requestedTriggers: notificationTriggers&lt;br /&gt;
&lt;br /&gt;
== notificationTriggerValidator ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate notificationTrigger&lt;br /&gt;
 * @param {Object} notificationTrigger&lt;br /&gt;
 * @param {string} notificationTrigger.propertyName - optional&lt;br /&gt;
 * @param {string} notificationTrigger.valueType - require&lt;br /&gt;
 * @param {string} notificationTrigger.valueString - optional&lt;br /&gt;
 * @param {number} notificationTrigger.valueNumber - optional&lt;br /&gt;
 * @param {Array} notificationTrigger.values - optional&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Array}&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# vatidate notificationTrigger (see. [[Service - Notification Manager]]) collect error and return status&lt;br /&gt;
#* notificationTrigger must have only one of those [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have ''propertyName''&lt;br /&gt;
#* validate type of properties [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#:- typeof &amp;quot;valueString&amp;quot; is ''string''&lt;br /&gt;
#:- typeof &amp;quot;valueNumber&amp;quot; is ''number''&lt;br /&gt;
#:- typeof &amp;quot;values&amp;quot; is ''array''&lt;br /&gt;
# return &lt;br /&gt;
#* validateStatus: {&amp;quot;complete&amp;quot; or &amp;quot;error&amp;quot;}&lt;br /&gt;
#* errorsFound[ ]&lt;br /&gt;
&lt;br /&gt;
== reformNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Reform notificationTriggers for send to InCreateTriggerGroup&lt;br /&gt;
 * @param {Object[]} notificationTriggers&lt;br /&gt;
 * &lt;br /&gt;
 * returns {Object[]}&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationTrigger of notificationTriggers&lt;br /&gt;
## notificationTrigger must have ''nodeLabels'' and ''nodeLabels'' is  &amp;quot;notificationTrigger&amp;quot;&lt;br /&gt;
## notificationTrigger must have ''nodeProperties'' &lt;br /&gt;
## use ''nodeProperties'' for [[#notificationTriggerValidator]]&lt;br /&gt;
## if ''error'';  throw new NoRetryError&lt;br /&gt;
## push ''nodeProperties'' to ''notificationTriggerReformed[ ]''&lt;br /&gt;
# return ''notificationTriggerReformed[ ]''&lt;br /&gt;
&lt;br /&gt;
== disableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships for find email&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
# change relationshipType to &amp;quot;disabled_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupDisabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
== enableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notificationTrigger&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
## change relationshipType to &amp;quot;has_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## use child notificationTriggers for [[#reformNotificationTrigger]] &lt;br /&gt;
## use function sharedCreateTriggerGroup.sharedCreateTriggerGroup&lt;br /&gt;
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup&lt;br /&gt;
###* uniqueId: notificationId&lt;br /&gt;
###* requestedTriggers: [{notificationTriggers}]&lt;br /&gt;
## send message to OutNotificationEnabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# change relationshipType to &amp;quot;has_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupEnabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
= Sample notification object sent to receiver service = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	notificationBody: [&lt;br /&gt;
		{&lt;br /&gt;
			messageAttributes: {&lt;br /&gt;
				// xxx: yyy&lt;br /&gt;
			},&lt;br /&gt;
			message: {&lt;br /&gt;
				// full body of message received&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
	],&lt;br /&gt;
	uniqueId: {emailId}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2020-11-08]]&lt;br /&gt;
[[Category:Working documents - Notification Manager| 2020-11-08]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2673</id>
		<title>2020-11-08 - Notification Manager - Functions</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2673"/>
		<updated>2024-08-05T02:35:01Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Lambda Functions =&lt;br /&gt;
&lt;br /&gt;
== RcvActivityMsg ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Receives one activity message from Activity Switchboard, find matching notification, stores for consolidation or sends immediately&lt;br /&gt;
 * @param {Object[]} event.activityMsg&lt;br /&gt;
 * @param {Object} event.activityMsg.messageAttributes&lt;br /&gt;
 * @param {Object} event.activityMsg.message&lt;br /&gt;
 * @param {String} event.uniqueId - In NotificationMgr, uniqueId is notificationId&lt;br /&gt;
 * @param {Object[]} event.additionalData - additional params set in the trigger group&lt;br /&gt;
 * @param {number} event.time &lt;br /&gt;
 * @param {string} event.additionalData.notificationId - matches notificationId in notifications table&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to [[Service - Activity Switchboard]] OutTriggerGroupPassed topic, (? filter policy limit) receiverTag = {this service's izServiceName}&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
# get notification node by getNodeAndRelationship&lt;br /&gt;
#* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#* toNodeVersionedDataLabels: [&amp;quot;notificationGroupSettings&amp;quot;]&lt;br /&gt;
# if not found notification or notificationGroup node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# If have setting and consolidated = true&lt;br /&gt;
#* save ''activityMsg'' to ConsolidationPending table&lt;br /&gt;
# else &lt;br /&gt;
#* [[#sendNotificationToReceiver]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== DisableNotificationGroups ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId - null if not supplied&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotificationGroups topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else &lt;br /&gt;
#* use function [[#disableNotificationGroup]] &lt;br /&gt;
&lt;br /&gt;
== UpdateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Update a notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue&lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {boolean} enabled&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InUpdateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot; or &amp;quot;disabled_notification&amp;quot; (depend on ''enable'')&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else update notificationGroup node by send message to [[Service - Graph Handler]] InCreateVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties.notificationGroupId&lt;br /&gt;
#* versionDataStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSetting&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# if enabled = false&lt;br /&gt;
## use function [[#disableNotificationGroup]]&lt;br /&gt;
# if enabled = true&lt;br /&gt;
## [[#enableNotificationGroup]]&lt;br /&gt;
&lt;br /&gt;
== DisableNotifications ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string} userId &lt;br /&gt;
 * @param {string[]} notificationIds&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotifications topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationIds&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
## if not found notification node&lt;br /&gt;
##* throw new NoRetryError &lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag: process.env.iz_serviceName&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates one notification group&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {number} consolidatedNextSendDue &lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {string} recieverTag&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# create notificationGroupId by random UUID&lt;br /&gt;
# if ''consolidated'' = true, validate:&lt;br /&gt;
## consolidatedType must equal &amp;quot;overview&amp;quot; or &amp;quot;detailed&amp;quot;&lt;br /&gt;
## consolidatedSendIfEmpty set default is &amp;quot;false&amp;quot;&lt;br /&gt;
## consolidatedFrequency ..? (not sure, maybe use some sort of standard like cron)&lt;br /&gt;
# create notificationGroup by send message to [[Service - Graph Handler]] InCreateNodeWithVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupId, recieverTag}&lt;br /&gt;
#* versionedDatas&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSettings&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedNextSendDue, consolidatedSendIfEmpty}&lt;br /&gt;
# send message to OutNotificationGroupCreated topic, can include full notificationGroup object&lt;br /&gt;
&lt;br /&gt;
== CreateNotification ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates notification&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {Object} notification&lt;br /&gt;
 * @param {string} [notification.notificationName]&lt;br /&gt;
 * @param {Object[]} notification.triggers&lt;br /&gt;
 * @param {string} [notification.triggers.propertyName]&lt;br /&gt;
 * @param {string} notification.triggers.valueType - property|attribute|serviceName|topicName&lt;br /&gt;
 * @param {string} [notification.triggers.valueString] - either value or values will be set&lt;br /&gt;
  * @param {number} [notification.triggers.valueNumber] - either value or values will be set&lt;br /&gt;
 * @param {string[]} [notification.triggers.values] - either value or values will be set&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotifications topic&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* notificationGroupId cannot be empty string&lt;br /&gt;
* notifications cannot be an empty array? Can limit number of notifications in array?&lt;br /&gt;
    &lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by graphSharedLib.getNode&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupId}&lt;br /&gt;
#* versionedDataLabels: [&amp;quot;notificationGroupSettings&amp;quot;]&lt;br /&gt;
# if not found notificationGroup node&lt;br /&gt;
## send message to OutCreateNotificationsFailed topic&lt;br /&gt;
##* notificationGroupId&lt;br /&gt;
##* notifications&lt;br /&gt;
##* errorsFound: &amp;quot;NotificationGroupNotFound&amp;quot;&lt;br /&gt;
## return&lt;br /&gt;
# for each ''notification'' of ''notifications'':&lt;br /&gt;
## create ''notificationId'' by random UUID and put in ''notification'' object&lt;br /&gt;
## push ''notification'' in ''passBackNotifications[ ]''&lt;br /&gt;
# for each ''passBackNotifications[ ]''&lt;br /&gt;
## if notification have ''notificationName''&lt;br /&gt;
##* create message object for send to  [[Service - Graph Handler]] InCreateChildNodeWithVersionedData&lt;br /&gt;
## else &lt;br /&gt;
##* create message object for send to  [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
# create awaiting step by asyncFlowSharedLib.createAwaitingMutipleSteps&lt;br /&gt;
#* see. [[NPM module - izara-shared]] in AsyncFlowSharedLib topic&lt;br /&gt;
# add callingFlow to message object by &lt;br /&gt;
## create callingFlowConfing by callingFlowSharedLib.createCallingFlowConfig&lt;br /&gt;
##* callingFlow: &amp;quot;NotificationMgrTestCreateNotificationTriggers&amp;quot;&lt;br /&gt;
##* callingFlowProperties: {''awaitingStepId'', ''passbackNotifications''}&lt;br /&gt;
## add callingFlow to message object by callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject&lt;br /&gt;
# send message to [[Service - Graph Handler]] InCreateChildNodeWithVersionedData or InCreateChildNodeAndRelationship&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationTriggers ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates muti notificationTrigers&lt;br /&gt;
 * @param {Object} returnValue&lt;br /&gt;
 * @param {Object} returnValue.parentNodeStructure&lt;br /&gt;
 * @param {Object} returnValue.createChildNodeStructure&lt;br /&gt;
 * @param {Object} returnValue.createRelationshipStructure&lt;br /&gt;
 * @param {string} returnValue.createdByUserId&lt;br /&gt;
 * @param {string} status&lt;br /&gt;
 * @param {Array} errorsFound&lt;br /&gt;
 * @param {Object} passBackProperties&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to GraphHandlerTestOutCreateChildNodeAndRelationship and GraphHandlerTestOutCreateChildNodeWithVersionedData topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# find ''pendingStepIds'' by asyncFlowSharedLib.findPendingStepIdsAwaitingStep in &amp;quot;AwaitingMultipleSteps&amp;quot; table&lt;br /&gt;
# putItem in &amp;quot;CreateNotificationRecord&amp;quot; table&lt;br /&gt;
#* notificationRecordId: &amp;quot;createNotifications&amp;quot; + hash(''passBackNotifications'') (partitionKey)&lt;br /&gt;
#* awaitingStepId (sortKey)&lt;br /&gt;
#* createNotificationStatus: {''status''}&lt;br /&gt;
#* if ''status'' is &amp;quot;error&amp;quot;; save ''errorsFound'' to table&lt;br /&gt;
# for each ''pendingStepId''&lt;br /&gt;
## check awaitingMutipleStep by asyncFlowSharedLib.checkAllAwaitingStepsFinished&lt;br /&gt;
## if return ''false''; asyncFlowSharedLib.removeAwaitingMultipleStep&lt;br /&gt;
## if return ''true''&lt;br /&gt;
### query &amp;quot;CreateNotificationRecord&amp;quot; table&lt;br /&gt;
### let status = &amp;quot;complete&amp;quot;&lt;br /&gt;
### for each notificationRecord; check all ''createNotificationStatus''&lt;br /&gt;
###* if ''createNotificationStatus'' is &amp;quot;error&amp;quot;; collect errorsFound  and status = &amp;quot;error&amp;quot;&lt;br /&gt;
### if  status = &amp;quot;error&amp;quot; &lt;br /&gt;
###* send message to OutCreateNotificationsFailed&lt;br /&gt;
###** notificationGroupId&lt;br /&gt;
###** notifications&lt;br /&gt;
###** errorsFound&lt;br /&gt;
###* asyncFlowSharedLib.removeAwaitingMultipleStep&lt;br /&gt;
###* return&lt;br /&gt;
### for each notification of ''passBackNotifications'' (validate ???)&lt;br /&gt;
#### for each notificationTrigger of ''notification.triggers''&lt;br /&gt;
##### [[#notificationTriggerValidator]]&lt;br /&gt;
##### if ''validateResult'' is &amp;quot;error&amp;quot;&lt;br /&gt;
#####* send message to OutCreateNotificationsFailed&lt;br /&gt;
##### create ''notificationTriggerId'' by random UUID&lt;br /&gt;
##### create message object for send to  [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
##### create awaiting step by asyncFlowSharedLib.createAwaitingMutipleSteps&lt;br /&gt;
#####* see. [[NPM module - izara-shared]] in AsyncFlowSharedLib topic&lt;br /&gt;
##### add callingFlow to message object by &lt;br /&gt;
###### create callingFlowConfing by callingFlowSharedLib.createCallingFlowConfig&lt;br /&gt;
######* callingFlow: &amp;quot;NotificationMgrTestCreateNotificationsComplete&amp;quot;&lt;br /&gt;
######* callingFlowProperties: {''awaitingStepId'', ''notificationGroupId'', ''passbackNotifications''}&lt;br /&gt;
###### callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject&lt;br /&gt;
##### send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
### asyncFlowSharedLib.removeAwaitingMultipleStep&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== NotificationGroup/List ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Lists all notification groups for one primary key (notificationGroupId/uniqueId)&lt;br /&gt;
 * @param {string} receiverTag&lt;br /&gt;
 * @param {string} groupingId&lt;br /&gt;
 * @param {string} [uniqueIdPrefix=null]&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Object[]} array of objects, each object is one notification group&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrApi ===&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* receiverTag&lt;br /&gt;
* groupingId&lt;br /&gt;
* uniqueIdPrefix with default value&lt;br /&gt;
&lt;br /&gt;
==== handler logic ====&lt;br /&gt;
&lt;br /&gt;
(standard)&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
Query NotificationGroups table for all records that match notificationGroupId and if uniqueIdPrefix not null uniqueId ''starts_with'' uniqueIdPrefix, and return.&lt;br /&gt;
&lt;br /&gt;
= Functions =&lt;br /&gt;
&lt;br /&gt;
== sendNotificationToReceiver ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notification - object from the Notifications table&lt;br /&gt;
 * @param {Object} notificationGroup - object from the NotificationGroups table&lt;br /&gt;
 * @param {string} notificationBody - string sent to receiver&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#:return; notificationGroup and email node (in future; will have another service eg. SMS, site notification)&lt;br /&gt;
# send message to OutNewNotification topic, adding below properties:&lt;br /&gt;
#* uniqueId: {emailId}&lt;br /&gt;
#* notificationBody&lt;br /&gt;
#* additionalData&lt;br /&gt;
#* (? filter policy limit) receiverTag gets added to message attributes so can be filtered for by receiving service&lt;br /&gt;
&lt;br /&gt;
;  [[Service - User Contact Manager]] and [[Service - Contact Method Email]] will change logic or structure &lt;br /&gt;
&lt;br /&gt;
== sharedCreateTriggerGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notificationId&lt;br /&gt;
 * @param {Array} notificationTriggers&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# send message to ActySwichboardTestInCreateTriggerGroup topic, adding below properties:&lt;br /&gt;
#* receiverTag: {process.env.iz_serviceName}&lt;br /&gt;
#* uniqueId: {notificationId}&lt;br /&gt;
#* additionalData: { }&lt;br /&gt;
#* requestedTriggers: notificationTriggers&lt;br /&gt;
&lt;br /&gt;
== notificationTriggerValidator ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate notificationTrigger &lt;br /&gt;
 * @param {Array} notificationTrigger&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# vatidate notificationTrigger (see. [[Service - Notification Manager]]) collect error and return status&lt;br /&gt;
#* notificationTrigger must have only one of those [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
#* validate type of properties [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#:- typeof &amp;quot;valueString&amp;quot; is ''string''&lt;br /&gt;
#:- typeof &amp;quot;valueNumber&amp;quot; is ''number''&lt;br /&gt;
#:- typeof &amp;quot;values&amp;quot; is ''array''&lt;br /&gt;
# return &lt;br /&gt;
#* validateStatus: {&amp;quot;complete&amp;quot; or &amp;quot;error&amp;quot;}&lt;br /&gt;
#* errorsFound[ ]&lt;br /&gt;
&lt;br /&gt;
== reformNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Reform notificationTriggers for send to InCreateTriggerGroup&lt;br /&gt;
 * @param {Array} notificationTriggers&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationTrigger of notificationTriggers&lt;br /&gt;
## notificationTrigger must have ''nodeLabels'' and ''nodeLabels'' is  &amp;quot;notificationTrigger&amp;quot;&lt;br /&gt;
## notificationTrigger must have ''nodeProperties'' &lt;br /&gt;
## use ''nodeProperties'' for [[#notificationTriggerValidator]]&lt;br /&gt;
## if ''error'';  throw new NoRetryError&lt;br /&gt;
## push ''nodeProperties'' to ''notificationTriggerReformed[ ]''&lt;br /&gt;
# return ''notificationTriggerReformed[ ]''&lt;br /&gt;
&lt;br /&gt;
== disableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships for find email&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
# change relationshipType to &amp;quot;disabled_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupDisabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
== enableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notificationTrigger&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
## change relationshipType to &amp;quot;has_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## use function sharedCreateTriggerGroup.sharedCreateTriggerGroup&lt;br /&gt;
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup&lt;br /&gt;
###* uniqueId: notificationId&lt;br /&gt;
###* additionalData: { }&lt;br /&gt;
###* requestedTriggers: [{notificationTriggers}]&lt;br /&gt;
## send message to OutNotificationEnabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# change relationshipType to &amp;quot;has_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupEnabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
= Sample notification object sent to receiver service = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	notificationBody: [&lt;br /&gt;
		{&lt;br /&gt;
			messageAttributes: {&lt;br /&gt;
				// xxx: yyy&lt;br /&gt;
			},&lt;br /&gt;
			message: {&lt;br /&gt;
				// full body of message received&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
	],&lt;br /&gt;
	additionalData: {&lt;br /&gt;
		userId: ...&lt;br /&gt;
	},&lt;br /&gt;
	uniqueId: {emailId}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2020-11-08]]&lt;br /&gt;
[[Category:Working documents - Notification Manager| 2020-11-08]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2672</id>
		<title>Service - Notification Manager</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=Service_-_Notification_Manager&amp;diff=2672"/>
		<updated>2024-08-05T02:34:53Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
= Repository =&lt;br /&gt;
&lt;br /&gt;
https://bitbucket.org/stb_working/notification-manager/src/master/&lt;br /&gt;
&lt;br /&gt;
= DynamoDB tables =&lt;br /&gt;
&lt;br /&gt;
== PendingConsolidation Table ==&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: type: string&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: type: number&lt;br /&gt;
: {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
; activityMsg&lt;br /&gt;
: type: object&lt;br /&gt;
: object containing ''messageAttributes'' and ''message''&lt;br /&gt;
&lt;br /&gt;
= Object Schemas =&lt;br /&gt;
&lt;br /&gt;
; Additional Information: [[Per Service Schemas]]&lt;br /&gt;
&lt;br /&gt;
== objType ==&lt;br /&gt;
&lt;br /&gt;
=== notificationGroup ===&lt;br /&gt;
&lt;br /&gt;
* Groups many notificatons together&lt;br /&gt;
* Simplifies management of similar notifications&lt;br /&gt;
* Allows for consolidation of notifications at notification group level&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationGroup&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationGroupSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationGroupName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{ &lt;br /&gt;
					fieldName: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					requiredOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedType&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedFrequency&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedNextSendDue&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;number&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 								&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					fieldName: &amp;quot;consolidatedSendIfEmpty&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				 				&lt;br /&gt;
					canUpdate: true, 						&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;update&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: { // see Per Service Schemas&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		receiverTag: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; notificationGroupName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; receiverTag&lt;br /&gt;
: set by the creating service&lt;br /&gt;
: eg ContactMethodEmail&lt;br /&gt;
; consolidated&lt;br /&gt;
: true|false&lt;br /&gt;
: optional, if excluded (or value not true), defaults to false&lt;br /&gt;
; consolidatedType&lt;br /&gt;
: overview|detailed&lt;br /&gt;
: ''overview'' sends a count of each type of notification, ''detailed'' lists each activity message&lt;br /&gt;
; consolidatedFrequency&lt;br /&gt;
: not sure, maybe use some sort of standard like cron&lt;br /&gt;
; consolidatedNextSendDue&lt;br /&gt;
: timestamp for next due time to send notification&lt;br /&gt;
; consolidatedSendIfEmpty&lt;br /&gt;
: true|false&lt;br /&gt;
; userId&lt;br /&gt;
: for createdBy relationship&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== notification ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notification&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
	complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
	addOnDataStructure: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;versionedData&amp;quot;,&lt;br /&gt;
			versionedDataLabel: &amp;quot;notificationSettings&amp;quot;,&lt;br /&gt;
			storageResourceTag: &amp;quot;myGraph&amp;quot;,&lt;br /&gt;
			fieldNames: [&lt;br /&gt;
				{ // fieldName in versionedData should now same in main objectSchema&lt;br /&gt;
			 		fieldName: &amp;quot;notificationName&amp;quot;,&lt;br /&gt;
			 		type: &amp;quot;string&amp;quot;,&lt;br /&gt;
					optionalOnCreate: true, 				// default = false&lt;br /&gt;
					canUpdate: true, 						// default = true&lt;br /&gt;
					validation: {&lt;br /&gt;
						pattern: pattern&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			]&lt;br /&gt;
		}&lt;br /&gt;
    ],&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;get&amp;quot;, &amp;quot;update&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationId: {&lt;br /&gt;
		    type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; notificationName&lt;br /&gt;
: string name set by reciever service, optional&lt;br /&gt;
; userId&lt;br /&gt;
: for createdBy relationship&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== notificationTrigger ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;notificationTrigger&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    overwriteGeneratedMainFunction: [&amp;quot;create&amp;quot;, &amp;quot;get&amp;quot;],&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		myGraph: {&lt;br /&gt;
			storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
			graphServerTag: &amp;quot;GraphHandler&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationTriggerId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		propertyName: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueType: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		},&lt;br /&gt;
		valueString: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		valueNumber: {&lt;br /&gt;
			type: &amp;quot;number&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		values: {&lt;br /&gt;
			type: &amp;quot;arrayMixed&amp;quot;,&lt;br /&gt;
			optionalOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']		&lt;br /&gt;
		},&lt;br /&gt;
		userId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['myGraph']&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;identifier&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationTriggerId&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationTriggerId (identifier)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; propertyName&lt;br /&gt;
: can be a nested property, use dot notation&lt;br /&gt;
: &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
; valueType&lt;br /&gt;
: property|attribute|serviceName|topicName&lt;br /&gt;
; valueString&lt;br /&gt;
: type: string&lt;br /&gt;
; valueNumber&lt;br /&gt;
: type: number&lt;br /&gt;
; values&lt;br /&gt;
: type: arrayMixed&lt;br /&gt;
; userId&lt;br /&gt;
: for createdBy relationship&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== consolidated ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	objectType: &amp;quot;consolidated&amp;quot;,&lt;br /&gt;
	canDelete: false,&lt;br /&gt;
    complexFilterServiceTag: &amp;quot;complexFilter&amp;quot;,&lt;br /&gt;
    storageResources: {&lt;br /&gt;
		dynamoDB: {&lt;br /&gt;
			storageType: &amp;quot;dynamoDB&amp;quot;,&lt;br /&gt;
			tableName: &amp;quot;PendingConsolidation&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    fieldNames: {&lt;br /&gt;
		notificationGroupId: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB'],&lt;br /&gt;
			fromServiceNameTag: &amp;quot;NotificationMgr&amp;quot; ,&lt;br /&gt;
			fromObjectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		time: {&lt;br /&gt;
			type: &amp;quot;string&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		},&lt;br /&gt;
		activityMsg: {&lt;br /&gt;
			type: &amp;quot;object&amp;quot;,&lt;br /&gt;
			requiredOnCreate: true,&lt;br /&gt;
			canUpdate: false,&lt;br /&gt;
			validation: {&lt;br /&gt;
				pattern: pattern&lt;br /&gt;
			},&lt;br /&gt;
			storageResourceTags: ['dynamoDB']		&lt;br /&gt;
		}&lt;br /&gt;
    },&lt;br /&gt;
    identifiers: [&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;partitionKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;notificationGroupId&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			type: &amp;quot;sortKey&amp;quot;,&lt;br /&gt;
			fieldName: &amp;quot;time&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== fieldNames ====&lt;br /&gt;
&lt;br /&gt;
; notificationGroupId (partition key)&lt;br /&gt;
: comes from: random UUID&lt;br /&gt;
; time (sort key)&lt;br /&gt;
: comes from {timestamp activity handled in Activity Switchboard}_{small random UUID}&lt;br /&gt;
: adding the UUID to ensure no clashing records with the same timestamp and notificationGroupId&lt;br /&gt;
; activityMsg&lt;br /&gt;
: copy of the message delivered from Activity Switchboard&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Object Relationships ==&lt;br /&gt;
&lt;br /&gt;
=== has_notificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;has_notificationGroup&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;UserContactManager&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;userContact&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
: links email to notificationGroup&lt;br /&gt;
&lt;br /&gt;
=== disabled_notificationGroup ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabled_notificationGroup&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            	// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          	// default = true&lt;br /&gt;
				validation: {}            	// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;ContactMethodEmail&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;email&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;					&lt;br /&gt;
					},			&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== has_notification ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;has_notification&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            		// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          		// default = true&lt;br /&gt;
				validation: {}            			// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;GraphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true					&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,					&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== disabled_notification ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabled_notification&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            		// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          		// default = true&lt;br /&gt;
				validation: {}            			// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== has_notificationTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;has_notificationTrigger&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            		// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          		// default = true&lt;br /&gt;
				validation: {}            			// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationTrigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					requiredOnCreate: true,	&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== disabled_notificationTrigger ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;disabled_notificationTrigger&amp;quot;: {&lt;br /&gt;
		properties: {&lt;br /&gt;
			&amp;quot;originTimestamp&amp;quot;: {&lt;br /&gt;
			    type: &amp;quot;number&amp;quot;,            		// &amp;quot;string&amp;quot; | &amp;quot;number&amp;quot; ...&lt;br /&gt;
				requiredOnCreate: true,  	// default=false&lt;br /&gt;
				canUpdate: true,          		// default = true&lt;br /&gt;
				validation: {}            			// ajv syntax&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		storageResources: {&lt;br /&gt;
			myGraph: {&lt;br /&gt;
				storageType: &amp;quot;graph&amp;quot;,&lt;br /&gt;
				graphServerTag: &amp;quot;graphHandler&amp;quot;			&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		links: [&lt;br /&gt;
			{&lt;br /&gt;
				storageResourceTags: [&amp;quot;myGraph&amp;quot;],&lt;br /&gt;
				from: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,					&lt;br /&gt;
						objectType: &amp;quot;notification&amp;quot;&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;one&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				},&lt;br /&gt;
				to: {&lt;br /&gt;
					objType: {&lt;br /&gt;
						serviceTag: &amp;quot;NotificationMgr&amp;quot;,				&lt;br /&gt;
						objectType: &amp;quot;notificationTrigger&amp;quot;					&lt;br /&gt;
					},&lt;br /&gt;
					linkType: &amp;quot;many&amp;quot;,&lt;br /&gt;
					handler: true&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		]&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== value or values array ==&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* 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.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName &lt;br /&gt;
&lt;br /&gt;
= serviceName and topicName triggers =&lt;br /&gt;
&lt;br /&gt;
* Notifications do not have to set their serviceName and/or topicName, if not set then any message that matches the properties will pass&lt;br /&gt;
* Can set only the serviceName, then any message that passess the properties and is from that serviceName passes&lt;br /&gt;
* Can set serviceName+topicName, then only messages in that topic will pass&lt;br /&gt;
* serviceName and topicName can both be submitted as &amp;quot;values&amp;quot; array, creating multiple trigger groups for each combination&lt;br /&gt;
&lt;br /&gt;
= Consolidated notifications =&lt;br /&gt;
&lt;br /&gt;
== How to trigger processing for time based consolidations == &lt;br /&gt;
&lt;br /&gt;
* ....&lt;br /&gt;
* 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)&lt;br /&gt;
&lt;br /&gt;
= Formating the notification body = &lt;br /&gt;
&lt;br /&gt;
* Initially the format will be hardwired and simple, but in the future we could create templates for formating the notification body&lt;br /&gt;
* The notificationBody is currently sent to receiver as a JSON stringified object containing all activityMsgs&lt;br /&gt;
&lt;br /&gt;
= Ideas = &lt;br /&gt;
&lt;br /&gt;
* ''overview'' notifications currently count per notification, but could be extended to do aggregates on a per notification, per field level&lt;br /&gt;
* 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&lt;br /&gt;
&lt;br /&gt;
= Working documents =&lt;br /&gt;
&lt;br /&gt;
[[:Category:Working documents - Notification Manager|Working documents - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Backend services| Notification Manager]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2671</id>
		<title>2020-11-08 - Notification Manager - Functions</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2671"/>
		<updated>2024-07-31T08:56:01Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Lambda Functions =&lt;br /&gt;
&lt;br /&gt;
== RcvActivityMsg ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Receives one activity message from Activity Switchboard, find matching notification, stores for consolidation or sends immediately&lt;br /&gt;
 * @param {Object[]} event.activityMsg&lt;br /&gt;
 * @param {Object} event.activityMsg.messageAttributes&lt;br /&gt;
 * @param {Object} event.activityMsg.message&lt;br /&gt;
 * @param {String} event.uniqueId - In NotificationMgr, uniqueId is notificationId&lt;br /&gt;
 * @param {Object[]} event.additionalData - additional params set in the trigger group&lt;br /&gt;
 * @param {number} event.time &lt;br /&gt;
 * @param {string} event.additionalData.notificationId - matches notificationId in notifications table&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to [[Service - Activity Switchboard]] OutTriggerGroupPassed topic, (? filter policy limit) receiverTag = {this service's izServiceName}&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
# get notification node by getNodeAndRelationship&lt;br /&gt;
#* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#* toNodeVersionedDataLabels: [&amp;quot;notificationGroupSettings&amp;quot;]&lt;br /&gt;
# if not found notification or notificationGroup node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# If have setting and consolidated = true&lt;br /&gt;
#* save ''activityMsg'' to ConsolidationPending table&lt;br /&gt;
# else &lt;br /&gt;
#* [[#sendNotificationToReceiver]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== DisableNotificationGroups ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string} notificationGroupId - null if not supplied&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotificationGroups topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else &lt;br /&gt;
#* use function [[#disableNotificationGroup]] &lt;br /&gt;
&lt;br /&gt;
== UpdateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Update a notification group&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {boolean} enabled&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InUpdateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot; or &amp;quot;disabled_notification&amp;quot; (depend on ''enable'')&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else update notificationGroup node by send message to [[Service - Graph Handler]] InCreateVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties.notificationGroupId&lt;br /&gt;
#* versionDataStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSetting&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedSendIfEmpty}&lt;br /&gt;
# if enabled = false&lt;br /&gt;
## use function [[#disableNotificationGroup]]&lt;br /&gt;
# if enabled = true&lt;br /&gt;
## [[#enableNotificationGroup]]&lt;br /&gt;
&lt;br /&gt;
== DisableNotifications ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string[]} notificationIds&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotifications topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationIds&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
## if not found notification node&lt;br /&gt;
##* throw new NoRetryError &lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag: process.env.iz_serviceName&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates one notification group&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {Object} additionalData&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* notificationGroupId cannot be empty string&lt;br /&gt;
    &lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# if ''consolidated'' = true, validate:&lt;br /&gt;
## consolidatedType must equal &amp;quot;overview&amp;quot;|&amp;quot;detailed&amp;quot;&lt;br /&gt;
## consolidatedFrequency ..?&lt;br /&gt;
## consolidatedSendIfEmpty, set to false if not boolean and set to true&lt;br /&gt;
# create notificationGroup by send message to [[Service - Graph Handler]] InCreateNodeWithVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupId, additionalData}&lt;br /&gt;
#* versionedDatas&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSetting&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedSendIfEmpty}&lt;br /&gt;
# send message to OutNotificationGroupCreated topic, can include full notificationGroup object&lt;br /&gt;
&lt;br /&gt;
== CreateNotifications ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates muti notifications&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {Object[]} notifications&lt;br /&gt;
 * @param {string} [notifications.notificationName]&lt;br /&gt;
 * @param {Object[]} notifications.triggers&lt;br /&gt;
 * @param {string} [notifications.triggers.propertyName]&lt;br /&gt;
 * @param {string} notifications.triggers.valueType - property|attribute|serviceName|topicName&lt;br /&gt;
 * @param {string} [notifications.triggers.valueString] - either value or values will be set&lt;br /&gt;
  * @param {number} [notifications.triggers.valueNumber] - either value or values will be set&lt;br /&gt;
 * @param {string[]} [notifications.triggers.values] - either value or values will be set&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotifications topic&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* notificationGroupId cannot be empty string&lt;br /&gt;
* notifications cannot be an empty array? Can limit number of notifications in array?&lt;br /&gt;
    &lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by graphSharedLib.getNode&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupId}&lt;br /&gt;
#* versionedDataLabels: [&amp;quot;notificationGroupSettings&amp;quot;]&lt;br /&gt;
# if not found notificationGroup node&lt;br /&gt;
## send message to OutCreateNotificationsFailed topic&lt;br /&gt;
##* notificationGroupId&lt;br /&gt;
##* notifications&lt;br /&gt;
##* errorsFound: &amp;quot;NotificationGroupNotFound&amp;quot;&lt;br /&gt;
## return&lt;br /&gt;
# for each ''notification'' of ''notifications'':&lt;br /&gt;
## create ''notificationId'' by random UUID and put in ''notification'' object&lt;br /&gt;
## push ''notification'' in ''passBackNotifications[ ]''&lt;br /&gt;
# for each ''passBackNotifications[ ]''&lt;br /&gt;
## if notification have ''notificationName''&lt;br /&gt;
##* create message object for send to  [[Service - Graph Handler]] InCreateChildNodeWithVersionedData&lt;br /&gt;
## else &lt;br /&gt;
##* create message object for send to  [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
# create awaiting step by asyncFlowSharedLib.createAwaitingMutipleSteps&lt;br /&gt;
#* see. [[NPM module - izara-shared]] in AsyncFlowSharedLib topic&lt;br /&gt;
# add callingFlow to message object by &lt;br /&gt;
## create callingFlowConfing by callingFlowSharedLib.createCallingFlowConfig&lt;br /&gt;
##* callingFlow: &amp;quot;NotificationMgrTestCreateNotificationTriggers&amp;quot;&lt;br /&gt;
##* callingFlowProperties: {''awaitingStepId'', ''passbackNotifications''}&lt;br /&gt;
## add callingFlow to message object by callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject&lt;br /&gt;
# send message to [[Service - Graph Handler]] InCreateChildNodeWithVersionedData or InCreateChildNodeAndRelationship&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationTriggers ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates muti notificationTrigers&lt;br /&gt;
 * @param {Object} returnValue&lt;br /&gt;
 * @param {Object} returnValue.parentNodeStructure&lt;br /&gt;
 * @param {Object} returnValue.createChildNodeStructure&lt;br /&gt;
 * @param {Object} returnValue.createRelationshipStructure&lt;br /&gt;
 * @param {string} returnValue.createdByUserId&lt;br /&gt;
 * @param {string} status&lt;br /&gt;
 * @param {Array} errorsFound&lt;br /&gt;
 * @param {Object} passBackProperties&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to GraphHandlerTestOutCreateChildNodeAndRelationship and GraphHandlerTestOutCreateChildNodeWithVersionedData topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# find ''pendingStepIds'' by asyncFlowSharedLib.findPendingStepIdsAwaitingStep in &amp;quot;AwaitingMultipleSteps&amp;quot; table&lt;br /&gt;
# putItem in &amp;quot;CreateNotificationRecord&amp;quot; table&lt;br /&gt;
#* notificationRecordId: &amp;quot;createNotifications&amp;quot; + hash(''passBackNotifications'') (partitionKey)&lt;br /&gt;
#* awaitingStepId (sortKey)&lt;br /&gt;
#* createNotificationStatus: {''status''}&lt;br /&gt;
#* if ''status'' is &amp;quot;error&amp;quot;; save ''errorsFound'' to table&lt;br /&gt;
# for each ''pendingStepId''&lt;br /&gt;
## check awaitingMutipleStep by asyncFlowSharedLib.checkAllAwaitingStepsFinished&lt;br /&gt;
## if return ''false''; asyncFlowSharedLib.removeAwaitingMultipleStep&lt;br /&gt;
## if return ''true''&lt;br /&gt;
### query &amp;quot;CreateNotificationRecord&amp;quot; table&lt;br /&gt;
### let status = &amp;quot;complete&amp;quot;&lt;br /&gt;
### for each notificationRecord; check all ''createNotificationStatus''&lt;br /&gt;
###* if ''createNotificationStatus'' is &amp;quot;error&amp;quot;; collect errorsFound  and status = &amp;quot;error&amp;quot;&lt;br /&gt;
### if  status = &amp;quot;error&amp;quot; &lt;br /&gt;
###* send message to OutCreateNotificationsFailed&lt;br /&gt;
###** notificationGroupId&lt;br /&gt;
###** notifications&lt;br /&gt;
###** errorsFound&lt;br /&gt;
###* asyncFlowSharedLib.removeAwaitingMultipleStep&lt;br /&gt;
###* return&lt;br /&gt;
### for each notification of ''passBackNotifications'' (validate ???)&lt;br /&gt;
#### for each notificationTrigger of ''notification.triggers''&lt;br /&gt;
##### [[#notificationTriggerValidator]]&lt;br /&gt;
##### create ''notificationTriggerId'' by random UUID&lt;br /&gt;
##### create message object for send to  [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
##### create awaiting step by asyncFlowSharedLib.createAwaitingMutipleSteps&lt;br /&gt;
#####* see. [[NPM module - izara-shared]] in AsyncFlowSharedLib topic&lt;br /&gt;
##### add callingFlow to message object by &lt;br /&gt;
###### create callingFlowConfing by callingFlowSharedLib.createCallingFlowConfig&lt;br /&gt;
######* callingFlow: &amp;quot;NotificationMgrTestCreateNotificationsComplete&amp;quot;&lt;br /&gt;
######* callingFlowProperties: {''awaitingStepId'', ''notificationGroupId'', ''passbackNotifications''}&lt;br /&gt;
###### callingFlowSharedLib.addCallingFlowToSnsRequestMessageObject&lt;br /&gt;
##### send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
### asyncFlowSharedLib.removeAwaitingMultipleStep&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationsComplete ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates muti notificationTrigers&lt;br /&gt;
 * @param {Object} returnValue&lt;br /&gt;
 * @param {Object} returnValue.parentNodeStructure&lt;br /&gt;
 * @param {Object} returnValue.createChildNodeStructure&lt;br /&gt;
 * @param {Object} returnValue.createRelationshipStructure&lt;br /&gt;
 * @param {string} returnValue.createdByUserId&lt;br /&gt;
 * @param {string} status&lt;br /&gt;
 * @param {Array} errorsFound&lt;br /&gt;
 * @param {Object} passBackProperties&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to GraphHandlerTestOutCreateChildNodeAndRelationship&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# find ''pendingStepIds'' by asyncFlowSharedLib.findPendingStepIdsAwaitingStep in &amp;quot;AwaitingMultipleSteps&amp;quot; table&lt;br /&gt;
# putItem in &amp;quot;CreateNotificationRecord&amp;quot; table&lt;br /&gt;
#* notificationRecordId: &amp;quot;createNotificationTriggers&amp;quot; + hash(''passBackNotifications'') (partitionKey)&lt;br /&gt;
#* awaitingStepId (sortKey)&lt;br /&gt;
#* createNotificationStatus: {''status''}&lt;br /&gt;
#* if ''status'' is &amp;quot;error&amp;quot;; save ''errorsFound'' to table&lt;br /&gt;
# for each ''pendingStepId''&lt;br /&gt;
## check awaitingMutipleStep by asyncFlowSharedLib.checkAllAwaitingStepsFinished&lt;br /&gt;
## if return ''false''; asyncFlowSharedLib.removeAwaitingMultipleStep&lt;br /&gt;
## if return ''true''&lt;br /&gt;
### query &amp;quot;CreateNotificationRecord&amp;quot; table&lt;br /&gt;
### let status = &amp;quot;complete&amp;quot;&lt;br /&gt;
### for each notificationRecord; check all ''createNotificationStatus''&lt;br /&gt;
###* if ''createNotificationStatus'' is &amp;quot;error&amp;quot;; collect errorsFound  and status = &amp;quot;error&amp;quot;&lt;br /&gt;
### if  status = &amp;quot;error&amp;quot; &lt;br /&gt;
###* send message to OutCreateNotificationsFailed&lt;br /&gt;
###** notificationGroupId&lt;br /&gt;
###** notifications&lt;br /&gt;
###** errorsFound&lt;br /&gt;
###* asyncFlowSharedLib.removeAwaitingMultipleStep&lt;br /&gt;
###* return&lt;br /&gt;
### for each notification of ''passBackNotifications''&lt;br /&gt;
#### use function sharedCreateTriggerGroup.sharedCreateTriggerGroup [[#sharedCreateTriggerGroup]] &lt;br /&gt;
#### send message to OutCreateNotificationsFailed&lt;br /&gt;
####* notification&lt;br /&gt;
### asyncFlowSharedLib.removeAwaitingMultipleStep&lt;br /&gt;
&lt;br /&gt;
== NotificationGroup/List ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Lists all notification groups for one primary key (notificationGroupId/uniqueId)&lt;br /&gt;
 * @param {string} receiverTag&lt;br /&gt;
 * @param {string} groupingId&lt;br /&gt;
 * @param {string} [uniqueIdPrefix=null]&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Object[]} array of objects, each object is one notification group&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrApi ===&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* receiverTag&lt;br /&gt;
* groupingId&lt;br /&gt;
* uniqueIdPrefix with default value&lt;br /&gt;
&lt;br /&gt;
==== handler logic ====&lt;br /&gt;
&lt;br /&gt;
(standard)&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
Query NotificationGroups table for all records that match notificationGroupId and if uniqueIdPrefix not null uniqueId ''starts_with'' uniqueIdPrefix, and return.&lt;br /&gt;
&lt;br /&gt;
= Functions =&lt;br /&gt;
&lt;br /&gt;
== sendNotificationToReceiver ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notification - object from the Notifications table&lt;br /&gt;
 * @param {Object} notificationGroup - object from the NotificationGroups table&lt;br /&gt;
 * @param {string} notificationBody - string sent to receiver&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#:return; notificationGroup and email node (in future; will have another service eg. SMS, site notification)&lt;br /&gt;
# send message to OutNewNotification topic, adding below properties:&lt;br /&gt;
#* uniqueId: {emailId}&lt;br /&gt;
#* notificationBody&lt;br /&gt;
#* additionalData&lt;br /&gt;
#* (? filter policy limit) receiverTag gets added to message attributes so can be filtered for by receiving service&lt;br /&gt;
&lt;br /&gt;
;  [[Service - User Contact Manager]] and [[Service - Contact Method Email]] will change logic or structure &lt;br /&gt;
&lt;br /&gt;
== sharedCreateTriggerGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notificationId&lt;br /&gt;
 * @param {Array} notificationTriggers&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# send message to ActySwichboardTestInCreateTriggerGroup topic, adding below properties:&lt;br /&gt;
#* receiverTag: {process.env.iz_serviceName}&lt;br /&gt;
#* uniqueId: {notificationId}&lt;br /&gt;
#* additionalData: { }&lt;br /&gt;
#* requestedTriggers: notificationTriggers&lt;br /&gt;
&lt;br /&gt;
== notificationTriggerValidator ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate notificationTrigger &lt;br /&gt;
 * @param {Array} notificationTrigger&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# vatidate notificationTrigger (see. [[Service - Notification Manager]])&lt;br /&gt;
#* notificationTrigger must have only one of those [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#* &amp;quot;serviceName&amp;quot; and &amp;quot;topicName&amp;quot; valueTypes do not have propertyName&lt;br /&gt;
#* validate type of properties [&amp;quot;valueString&amp;quot;, &amp;quot;valueNumber&amp;quot;, &amp;quot;values&amp;quot;]&lt;br /&gt;
#:- typeof &amp;quot;valueString&amp;quot; is ''string''&lt;br /&gt;
#:- typeof &amp;quot;valueNumber&amp;quot; is ''number''&lt;br /&gt;
#:- typeof &amp;quot;values&amp;quot; is ''array''&lt;br /&gt;
&lt;br /&gt;
== reformNotificationTrigger ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Reform notificationTriggers for send to InCreateTriggerGroup&lt;br /&gt;
 * @param {Array} notificationTriggers&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== disableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* receiverTag&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships for find email&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
# change relationshipType to &amp;quot;disabled_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupDisabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
== enableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notificationTrigger&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
## change relationshipType to &amp;quot;has_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## use function sharedCreateTriggerGroup.sharedCreateTriggerGroup&lt;br /&gt;
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup&lt;br /&gt;
###* uniqueId: notificationId&lt;br /&gt;
###* additionalData: { }&lt;br /&gt;
###* requestedTriggers: [{notificationTriggers}]&lt;br /&gt;
## send message to OutNotificationEnabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# change relationshipType to &amp;quot;has_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupEnabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
= Sample notification object sent to receiver service = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	notificationBody: [&lt;br /&gt;
		{&lt;br /&gt;
			messageAttributes: {&lt;br /&gt;
				// xxx: yyy&lt;br /&gt;
			},&lt;br /&gt;
			message: {&lt;br /&gt;
				// full body of message received&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
	],&lt;br /&gt;
	additionalData: {&lt;br /&gt;
		userId: ...&lt;br /&gt;
	},&lt;br /&gt;
	uniqueId: {emailId}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2020-11-08]]&lt;br /&gt;
[[Category:Working documents - Notification Manager| 2020-11-08]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
	<entry>
		<id>https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2666</id>
		<title>2020-11-08 - Notification Manager - Functions</title>
		<link rel="alternate" type="text/html" href="https://izara.io/wiki/index.php?title=2020-11-08_-_Notification_Manager_-_Functions&amp;diff=2666"/>
		<updated>2024-07-18T09:08:46Z</updated>

		<summary type="html">&lt;p&gt;Pong: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Service - Notification Manager]]&lt;br /&gt;
&lt;br /&gt;
= Lambda Functions =&lt;br /&gt;
&lt;br /&gt;
== RcvActivityMsg ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Receives one activity message from Activity Switchboard, find matching notification, stores for consolidation or sends immediately&lt;br /&gt;
 * @param {Object[]} event.activityMsg&lt;br /&gt;
 * @param {Object} event.activityMsg.messageAttributes&lt;br /&gt;
 * @param {Object} event.activityMsg.message&lt;br /&gt;
 * @param {String} event.uniqueId - In NotificationMgr, uniqueId is notificationId&lt;br /&gt;
 * @param {Object[]} event.additionalData - additional params set in the trigger group&lt;br /&gt;
 * @param {number} event.time &lt;br /&gt;
 * @param {string} event.additionalData.notificationId - matches notificationId in notifications table&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to [[Service - Activity Switchboard]] OutTriggerGroupPassed topic, (? filter policy limit) receiverTag = {this service's izServiceName}&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
# get notification node by getNodeAndRelationship&lt;br /&gt;
#* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#* toNodeVersionedDataLabels: [&amp;quot;notificationGroupSettings&amp;quot;]&lt;br /&gt;
# if not found notification or notificationGroup node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# If have setting and consolidated = true&lt;br /&gt;
#* save ''activityMsg'' to ConsolidationPending table&lt;br /&gt;
# else &lt;br /&gt;
#* [[#sendNotificationToReceiver]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== DisableNotificationGroups ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string} notificationGroupId - null if not supplied&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotificationGroups topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else &lt;br /&gt;
#* use function [[#disableNotificationGroup]] &lt;br /&gt;
&lt;br /&gt;
== UpdateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Update a notification group&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {boolean} enabled&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InUpdateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
# if not found notificationGroup or notification node&lt;br /&gt;
#* throw new NoRetryError &lt;br /&gt;
# else &lt;br /&gt;
#* get notificationId for each child notification node&lt;br /&gt;
# update notificationGroup node by send message to [[Service - Graph Handler]] InCreateVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties.notificationGroupId&lt;br /&gt;
#* versionDataStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSetting&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedSendIfEmpty}&lt;br /&gt;
# if enabled = false&lt;br /&gt;
## use function [[#disableNotificationGroup]]&lt;br /&gt;
# if enabled = true&lt;br /&gt;
## [[#enableNotificationGroup]]&lt;br /&gt;
&lt;br /&gt;
== DisableNotifications ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Disables a set of matching notification groups&lt;br /&gt;
 * @param {string[]} notificationIds&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InDisableNotifications topic&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationIds&lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
&lt;br /&gt;
== CreateNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates one notification group&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {string} notificationGroupName&lt;br /&gt;
 * @param {boolean} consolidated&lt;br /&gt;
 * @param {string} consolidatedType&lt;br /&gt;
 * @param {..?} consolidatedFrequency&lt;br /&gt;
 * @param {boolean} consolidatedSendIfEmpty&lt;br /&gt;
 * @param {Object} additionalData&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotificationGroup topic&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* notificationGroupId cannot be empty string&lt;br /&gt;
    &lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# if ''consolidated'' = true, validate:&lt;br /&gt;
## consolidatedType must equal &amp;quot;overview&amp;quot;|&amp;quot;detailed&amp;quot;&lt;br /&gt;
## consolidatedFrequency ..?&lt;br /&gt;
## consolidatedSendIfEmpty, set to false if not boolean and set to true&lt;br /&gt;
# create notificationGroup by send message to [[Service - Graph Handler]] InCreateNodeWithVersionedData&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupId, additionalData}&lt;br /&gt;
#* versionedDatas&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroupSetting&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationGroupName, consolidated, consolidatedType, consolidatedFrequency, consolidatedSendIfEmpty}&lt;br /&gt;
# send message to OutNotificationGroupCreated topic, can include full notificationGroup object&lt;br /&gt;
&lt;br /&gt;
== CreateNotifications ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Creates muti notifications&lt;br /&gt;
 * @param {string} userId&lt;br /&gt;
 * @param {string} notificationGroupId&lt;br /&gt;
 * @param {Object[]} notifications&lt;br /&gt;
 * @param {string} [notifications.notificationName]&lt;br /&gt;
 * @param {Object[]} notifications.triggers&lt;br /&gt;
 * @param {string} [notifications.triggers.propertyName]&lt;br /&gt;
 * @param {string} notifications.triggers.valueType - property|attribute|serviceName|topicName&lt;br /&gt;
 * @param {string} [notifications.triggers.value] - either value or values will be set&lt;br /&gt;
 * @param {string[]} [notifications.triggers.values] - either value or values will be set&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrSqs ===&lt;br /&gt;
&lt;br /&gt;
Subscribes to InCreateNotifications topic&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* notificationGroupId cannot be empty string&lt;br /&gt;
* notifications cannot be an empty array? Can limit number of notifications in array?&lt;br /&gt;
    &lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by graphSharedLib.getNode&lt;br /&gt;
#* nodeStructure&lt;br /&gt;
#** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#** nodeProperties: {notificationId}&lt;br /&gt;
#* versionedDataLabels: [&amp;quot;notificationGroupSetting&amp;quot;]&lt;br /&gt;
# if not found notificationGroup node&lt;br /&gt;
## send message to OutCreateNotificationsFailed topic&lt;br /&gt;
##* notificationGroupId&lt;br /&gt;
##* notifications&lt;br /&gt;
##* errorsFound: &amp;quot;NotificationGroupNotFound&amp;quot;&lt;br /&gt;
## return&lt;br /&gt;
# for each ''notifications'':&lt;br /&gt;
## create an object that contains&lt;br /&gt;
##* notificationName&lt;br /&gt;
##* triggers&lt;br /&gt;
## hash this object into variable named ''notificationId''&lt;br /&gt;
## create child notification node and relationship&lt;br /&gt;
### send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
###* parentNodeStructure&lt;br /&gt;
###** nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
###** nodeProperties: {notificationGroupId}&lt;br /&gt;
###* createChildNodeStructure&lt;br /&gt;
###** nodeStructure&lt;br /&gt;
###*** nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
###*** nodePropertis: {notificationId}&lt;br /&gt;
###* createRelationshipStructure&lt;br /&gt;
###** relationshipStructure&lt;br /&gt;
###*** relationshipType: &amp;quot;has_notification&amp;quot;&lt;br /&gt;
###*** relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
## for each ''notifications.triggers'':&lt;br /&gt;
### create ''notificationTriggerId'' by {notificationId} + &amp;quot;_&amp;quot; + {randon UUID}&lt;br /&gt;
### create child notificationTrigger node and relationship&lt;br /&gt;
#### send message to [[Service - Graph Handler]] InCreateChildNodeAndRelationship&lt;br /&gt;
####* parentNodeStructure&lt;br /&gt;
####** nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
####** nodeProperties: {notificationId}&lt;br /&gt;
####* createChildNodeStructure&lt;br /&gt;
####** nodeStructure&lt;br /&gt;
####*** nodeLabel: &amp;quot;notificationTrigger&amp;quot;&lt;br /&gt;
####*** nodePropertis: {notificationTriggerId}&lt;br /&gt;
####* createRelationshipStructure&lt;br /&gt;
####** relationshipStructure&lt;br /&gt;
####*** relationshipType: &amp;quot;has_notificationTrigger&amp;quot;&lt;br /&gt;
####*** relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
## create an object that contains 'notificationId' and 'triggers' for sharedCreateTriggerGroup.sharedCreateTriggerGroup&lt;br /&gt;
## use function sharedCreateTriggerGroup.sharedCreateTriggerGroup&lt;br /&gt;
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup topic&lt;br /&gt;
###* uniqueId: {notificationId}&lt;br /&gt;
###* additionalData: {}&lt;br /&gt;
###* triggers: {notification.triggers}&lt;br /&gt;
## send message to OutNotificationCreated topic, can include full notification object&lt;br /&gt;
&lt;br /&gt;
== NotificationGroup/List ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Lists all notification groups for one primary key (notificationGroupId/uniqueId)&lt;br /&gt;
 * @param {string} receiverTag&lt;br /&gt;
 * @param {string} groupingId&lt;br /&gt;
 * @param {string} [uniqueIdPrefix=null]&lt;br /&gt;
 *&lt;br /&gt;
 * @returns {Object[]} array of objects, each object is one notification group&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HdrApi ===&lt;br /&gt;
&lt;br /&gt;
Validator:&lt;br /&gt;
* receiverTag&lt;br /&gt;
* groupingId&lt;br /&gt;
* uniqueIdPrefix with default value&lt;br /&gt;
&lt;br /&gt;
==== handler logic ====&lt;br /&gt;
&lt;br /&gt;
(standard)&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
Query NotificationGroups table for all records that match notificationGroupId and if uniqueIdPrefix not null uniqueId ''starts_with'' uniqueIdPrefix, and return.&lt;br /&gt;
&lt;br /&gt;
= Functions =&lt;br /&gt;
&lt;br /&gt;
== sendNotificationToReceiver ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Send message to receiver endpoint&lt;br /&gt;
 * @param {Object} notification - object from the Notifications table&lt;br /&gt;
 * @param {Object} notificationGroup - object from the NotificationGroups table&lt;br /&gt;
 * @param {string} notificationBody - string sent to receiver&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# get notificationGroup node by getNodeAndRelationships&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
#:return; notificationGroup and email node (in future; will have another service eg. SMS, site notification)&lt;br /&gt;
# send message to OutNewNotification topic, adding below properties:&lt;br /&gt;
#* uniqueId: {emailId}&lt;br /&gt;
#* notificationBody&lt;br /&gt;
#* additionalData&lt;br /&gt;
#* (? filter policy limit) receiverTag gets added to message attributes so can be filtered for by receiving service&lt;br /&gt;
&lt;br /&gt;
;  [[Service - User Contact Manager]] and [[Service - Contact Method Email]] will change logic or structure &lt;br /&gt;
&lt;br /&gt;
== disableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## change relationshipType to &amp;quot;disabled_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## send message to [[Service - Activity Switchboard]] InRemoveTriggerGroup&lt;br /&gt;
##* notificationId&lt;br /&gt;
## send message to OutNotificationDisabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# get notificationGroup node by GraphHandler getNodeAndRelationships for find email&lt;br /&gt;
#* nodeLabel: &amp;quot;notificationGroup&amp;quot;&lt;br /&gt;
#* nodeProperties.notificationGroupId&lt;br /&gt;
#* relationshipType: &amp;quot;has_notificationGroup&amp;quot;&lt;br /&gt;
#* relationshipDirection: &amp;quot;incoming&amp;quot;&lt;br /&gt;
# change relationshipType to &amp;quot;disabled_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupDisabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
== enableNotificationGroup ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @param {Object} notificationGroup&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== logic ===&lt;br /&gt;
&lt;br /&gt;
# for each notificationId&lt;br /&gt;
## get notification node by GraphHandler getNodeAndRelationships&lt;br /&gt;
##* nodeLabel: &amp;quot;notification&amp;quot;&lt;br /&gt;
##* nodeProperties.notificationId&lt;br /&gt;
##* relationshipType: &amp;quot;has_notificationTrigger&amp;quot;&lt;br /&gt;
##* relationshipDirection: &amp;quot;outgoing&amp;quot;&lt;br /&gt;
## change relationshipType to &amp;quot;has_notification&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
## use function sharedCreateTriggerGroup.sharedCreateTriggerGroup&lt;br /&gt;
### send message to [[Service - Activity Switchboard]] InCreateTriggerGroup&lt;br /&gt;
###* uniqueId: notificationId&lt;br /&gt;
###* additionalData: {}&lt;br /&gt;
###* requestedTriggers: [{notificationTriggers}]&lt;br /&gt;
## send message to OutNotificationEnabled topic&lt;br /&gt;
##* notificationId&lt;br /&gt;
# change relationshipType to &amp;quot;has_notificationGroup&amp;quot; by send message to [[Service - Graph Handler]] InChangeRelationshipType&lt;br /&gt;
# send message to OutNotificationGroupEnabled topic&lt;br /&gt;
#* notificationGroupId&lt;br /&gt;
&lt;br /&gt;
= Sample notification object sent to receiver service = &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	notificationBody: [&lt;br /&gt;
		{&lt;br /&gt;
			messageAttributes: {&lt;br /&gt;
				// xxx: yyy&lt;br /&gt;
			},&lt;br /&gt;
			message: {&lt;br /&gt;
				// full body of message received&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
	],&lt;br /&gt;
	additionalData: {&lt;br /&gt;
		userId: ...&lt;br /&gt;
	},&lt;br /&gt;
	uniqueId: {emailId}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Working documents| 2020-11-08]]&lt;br /&gt;
[[Category:Working documents - Notification Manager| 2020-11-08]]&lt;/div&gt;</summary>
		<author><name>Pong</name></author>
	</entry>
</feed>