Created
September 17, 2018 09:24
-
-
Save eduard93/84851600b5b94326b91bb8893d6e4094 to your computer and use it in GitHub Desktop.
Ensemble publish subscribe sample
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<Export generator="Cache" version="25" zv="Cache for Windows (x86-64) 2017.2 (Build 744U)" ts="2018-09-17 12:22:13"> | |
<Class name="Test.Alert"> | |
<Description> | |
Email notification about workflow tasks</Description> | |
<Super>%Persistent,Ens.Request</Super> | |
<TimeChanged>64587,65114.949429</TimeChanged> | |
<TimeCreated>64261,39066.833339</TimeCreated> | |
<Property name="to"> | |
<Description> | |
Where to send email</Description> | |
<Type>%String</Type> | |
<Parameter name="MAXLEN" value="500"/> | |
</Property> | |
<Property name="topic"> | |
<Description> | |
Header</Description> | |
<Type>%String</Type> | |
<Parameter name="MAXLEN" value="250"/> | |
</Property> | |
<Property name="username"> | |
<Description> | |
Login</Description> | |
<Type>%String</Type> | |
<Parameter name="MAXLEN" value="250"/> | |
</Property> | |
<Property name="text"> | |
<Description> | |
Mail body</Description> | |
<Type>%String</Type> | |
<Parameter name="MAXLEN" value="250000"/> | |
</Property> | |
<Storage name="Default"> | |
<Type>%Library.CacheStorage</Type> | |
<DataLocation>^Test.AlertD</DataLocation> | |
<DefaultData>AlertDefaultData</DefaultData> | |
<IdLocation>^Test.AlertD</IdLocation> | |
<IndexLocation>^Test.AlertI</IndexLocation> | |
<StreamLocation>^Test.AlertS</StreamLocation> | |
<Data name="AlertDefaultData"> | |
<Structure>listnode</Structure> | |
<Subscript/> | |
<Value name="1"> | |
<Value>%%CLASSNAME</Value> | |
</Value> | |
<Value name="2"> | |
<Value>to</Value> | |
</Value> | |
<Value name="3"> | |
<Value>topic</Value> | |
</Value> | |
<Value name="4"> | |
<Value>username</Value> | |
</Value> | |
<Value name="5"> | |
<Value>text</Value> | |
</Value> | |
</Data> | |
</Storage> | |
</Class> | |
<Class name="Test.EmailAlertOperation"> | |
<Description> | |
Send system alerts and workflow aperts by email</Description> | |
<Super>Ens.BusinessOperation</Super> | |
<TimeChanged>64908,44020.73895</TimeChanged> | |
<TimeCreated>64261,34393.334735</TimeCreated> | |
<Parameter name="ALERTDOMAIN"> | |
<Description> | |
Domain for system alerts</Description> | |
<Default>SYSTEM</Default> | |
</Parameter> | |
<Parameter name="ADAPTER"> | |
<Default>EnsLib.EMail.OutboundAdapter</Default> | |
</Parameter> | |
<Property name="SubscriptionBO"> | |
<Description> | |
Subscription operation</Description> | |
<Type>%String</Type> | |
<Required>1</Required> | |
<Parameter name="MAXLEN" value="250"/> | |
</Property> | |
<Parameter name="SETTINGS"> | |
<Default><![CDATA[SubscriptionBO:Basic:selector?context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId}]]></Default> | |
</Parameter> | |
<Method name="OnMessage"> | |
<FormalSpec>pRequest:%Library.Persistent,*pResponse:Ens.Response</FormalSpec> | |
<ReturnType>%Status</ReturnType> | |
<Implementation><![CDATA[ | |
#dim sc As %Status = $$$OK | |
set class = pRequest.%ClassName($$$YES) | |
if class = "Ens.AlertRequest" { | |
set sc = ..onAlert(pRequest,.pResponse) | |
} elseif class = "Test.Alert" { | |
set sc = ..onWorkflowAlert(pRequest,.pResponse) | |
} else { | |
set sc = $$$ERROR($$$GeneralError, "Invalid message class: " _ class) | |
} | |
quit sc | |
]]></Implementation> | |
</Method> | |
<Method name="onAlert"> | |
<FormalSpec>pRequest:Ens.AlertRequest,*pResponse:Ens.Response</FormalSpec> | |
<ReturnType>%Status</ReturnType> | |
<Implementation><![CDATA[ | |
#dim sc As %Status = $$$OK | |
#dim tMailMessage As %Net.MailMessage = ##class(%Net.MailMessage).%New() | |
set tMailMessage.Subject = "Production error from: " _ pRequest.SourceConfigName | |
set tMailMessage.Charset = "UTF-8" | |
if (pRequest.AlertDestination '= "") | |
{ | |
#; if the Ens.AlertRequest supplied an AlertDestination, then add it to the list of configured Recipients | |
set sc = ..Adapter.AddRecipients(tMailMessage, pRequest.AlertDestination) | |
if $$$ISERR(sc) quit sc | |
} | |
set sc = ..addDomainSubscribersToMessage(tMailMessage, ..#ALERTDOMAIN) | |
quit:$$$ISERR(sc) sc | |
set sc = tMailMessage.TextData.Write(pRequest.AlertText) | |
quit:$$$ISERR(sc) sc | |
quit ..Adapter.SendMail(tMailMessage) | |
]]></Implementation> | |
</Method> | |
<Method name="onWorkflowAlert"> | |
<FormalSpec>pRequest:Test.Alert,*pResponse:Ens.Response</FormalSpec> | |
<ReturnType>%Status</ReturnType> | |
<Implementation><![CDATA[ | |
#dim sc As %Status = $$$OK | |
#dim tMailMessage As %Net.MailMessage = ##class(%Net.MailMessage).%New() | |
set tMailMessage.Subject = "Task notification: " _ pRequest.topic | |
set tMailMessage.Charset = "UTF-8" | |
set tMailMessage.IsHTML = $$$YES | |
if (pRequest.to '= "") { | |
#; if the Ens.AlertRequest supplied an AlertDestination, then add it to the list of configured Recipients | |
set sc = ..Adapter.AddRecipients(tMailMessage, pRequest.to) | |
if $$$ISERR(sc) quit sc | |
} | |
set sc = tMailMessage.TextData.Write(pRequest.text) | |
quit:$$$ISERR(sc) sc | |
quit ..Adapter.SendMail(tMailMessage) | |
]]></Implementation> | |
</Method> | |
<Method name="addDomainSubscribersToMessage"> | |
<FormalSpec>message:%Net.MailMessage,domain:%String,topic:%String=""</FormalSpec> | |
<Implementation><![CDATA[ | |
set subRequest = ##class(EnsLib.PubSub.Request).%New() | |
set subRequest.Topic = topic | |
set subRequest.DomainName = domain | |
#dim subResponse As EnsLib.PubSub.Response | |
set sc = ..SendRequestSync(..SubscriptionBO, subRequest, .subResponse) | |
set mails = "" | |
for i=1:1:subResponse.TargetList.Count() { | |
#dim target As EnsLib.PubSub.Target | |
set target = subResponse.TargetList.GetAt(i) | |
set mails = mails _ target.Address _ "," | |
} | |
quit ..Adapter.AddRecipients(message, mails) | |
]]></Implementation> | |
</Method> | |
</Class> | |
<Class name="Test.EmailService"> | |
<Super>Ens.BusinessService</Super> | |
<TimeChanged>64908,44334.522329</TimeChanged> | |
<TimeCreated>64266,52300.987228</TimeCreated> | |
<Parameter name="ADAPTER"> | |
<Default>Ens.InboundAdapter</Default> | |
</Parameter> | |
<Property name="UnacceptedTime"> | |
<Description> | |
Time (in hours) while task can be unaccepted</Description> | |
<Type>%Integer</Type> | |
<InitialExpression>12</InitialExpression> | |
</Property> | |
<Property name="UncompletedTime"> | |
<Description> | |
Time (in hours) a task can be uncompleted</Description> | |
<Type>%Integer</Type> | |
<InitialExpression>24</InitialExpression> | |
</Property> | |
<Property name="URL"> | |
<Description> | |
Base workflow portal uri</Description> | |
<Type>%String</Type> | |
<InitialExpression>"http://workflow-portal/csp/workflow/index.csp"</InitialExpression> | |
</Property> | |
<Property name="ApprovalDomain"> | |
<Description> | |
Subscriptions domain for approval tasks</Description> | |
<Type>%String</Type> | |
<InitialExpression>"APPROVAL"</InitialExpression> | |
<Parameter name="MAXLEN" value="250"/> | |
</Property> | |
<Property name="SendBO"> | |
<Description> | |
Send operation (to send email for example)</Description> | |
<Type>%String</Type> | |
<Parameter name="MAXLEN" value="250"/> | |
</Property> | |
<Property name="SubscriptionBO"> | |
<Description> | |
Subscription operation</Description> | |
<Type>%String</Type> | |
<Required>1</Required> | |
<Parameter name="MAXLEN" value="250"/> | |
</Property> | |
<Parameter name="SETTINGS"> | |
<Default><![CDATA[SubscriptionBO:Basic:selector?context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},SendBO:Basic:selector?context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},ApprovalDomain:Basic,URL:Basic,UnacceptedTime:Basic,UncompletedTime:Basic]]></Default> | |
</Parameter> | |
<Method name="OnProcessInput"> | |
<FormalSpec>pInput:%RegisteredObject,*pOutput:%RegisteredObject</FormalSpec> | |
<ReturnType>%Status</ReturnType> | |
<Implementation><![CDATA[ | |
do ##class(Test.PPGEmail).%KillExtent() | |
set ts = $zdt($zts,3,1,3) | |
#dim sc As %Status = $$$OK | |
set sc = ..generateEmailData() | |
quit:$$$ISERR(sc) sc | |
do ..sendEmails() | |
do ..setTime(ts) | |
quit sc | |
]]></Implementation> | |
</Method> | |
<Method name="generateEmailData"> | |
<Description> | |
Populates Test.PPGEmail with "messages" to send</Description> | |
<ReturnType>%Status</ReturnType> | |
<Implementation><![CDATA[ | |
#dim sc As %Status = $$$OK | |
set sc = ..generateWorkflowData() | |
quit sc | |
]]></Implementation> | |
</Method> | |
<Method name="determineEmails"> | |
<Description> | |
Get email addresses by domain and topic.</Description> | |
<FormalSpec>domain:%String,topic:%String</FormalSpec> | |
<ReturnType>%List</ReturnType> | |
<Implementation><![CDATA[ | |
set subRequest = ##class(EnsLib.PubSub.Request).%New() | |
set subRequest.Topic = topic | |
set subRequest.DomainName = domain | |
do ..SendRequestSync(..SubscriptionBO, subRequest, .subResponse,, "Get subscribers for domain: " _ domain _ ", topic: " _ topic) | |
set mails = "" | |
for i=1:1:subResponse.TargetList.Count() { | |
#dim target As EnsLib.PubSub.Target | |
set target = subResponse.TargetList.GetAt(i) | |
set mails = mails _ $lb(target.Address) | |
} | |
return mails | |
]]></Implementation> | |
</Method> | |
<Method name="generateWorkflowData"> | |
<Description> | |
Populates Test.PPGEmail with workflow "messages" to send</Description> | |
<Implementation><![CDATA[ | |
#dim sc As %Status = $$$OK | |
set types = $lb("new", "unassigned", "uncompleted") | |
for i=1:1:$ll(types) { | |
set type = $lg(types, i) | |
set sc = ..generateWorkflowDataByType(type) | |
quit:$$$ISERR(sc) | |
} | |
quit sc | |
]]></Implementation> | |
</Method> | |
<Query name="newWorkflowTasks"> | |
<Type>%SQLQuery</Type> | |
<FormalSpec>time</FormalSpec> | |
<SqlQuery><![CDATA[SELECT TaskStatus_RoleName As topic, "%Subject" As text, "%Message" As message | |
FROM EnsLib_Workflow.TaskResponse | |
WHERE "%Status" = 'Unassigned' AND TaskStatus_TimeCreated >= :time | |
ORDER BY TaskStatus_RoleName]]></SqlQuery> | |
</Query> | |
<Query name="unassignedWorkflowTasks"> | |
<Type>%SQLQuery</Type> | |
<FormalSpec>hours</FormalSpec> | |
<SqlQuery><![CDATA[SELECT TaskStatus_RoleName As topic, "%Subject" As text, "%Message" As message | |
FROM EnsLib_Workflow.TaskResponse | |
WHERE "%Status" = 'Unassigned' AND DATEDIFF('hh', TaskStatus_TimeCreated, NOW()) >= :hours | |
ORDER BY TaskStatus_RoleName]]></SqlQuery> | |
</Query> | |
<Query name="uncompletedWorkflowTasks"> | |
<Type>%SQLQuery</Type> | |
<FormalSpec>time</FormalSpec> | |
<SqlQuery><![CDATA[SELECT TaskStatus_RoleName As topic, "%Subject" As text, "%Message" As message | |
FROM EnsLib_Workflow.TaskResponse | |
WHERE "%Status" = 'Assigned' AND DATEDIFF('hh', TaskStatus_TimeCreated, NOW()) >= :hours | |
ORDER BY TaskStatus_RoleName]]></SqlQuery> | |
</Query> | |
<Method name="generateWorkflowDataByType"> | |
<Description> | |
Populates Test.PPGEmail with "messages" to send</Description> | |
<FormalSpec>type:%String(VALUELIST="new,unassigned,uncompleted")</FormalSpec> | |
<ReturnType>%Status</ReturnType> | |
<Implementation><![CDATA[ | |
#dim sc As %Status = $$$OK | |
set prevTopic = "" | |
if type = "new" { | |
set query = "newWorkflowTasks" | |
set arg = ..getTime() | |
} elseif type = "unassigned" { | |
set query = "unassignedWorkflowTasks" | |
set arg = ..UnacceptedTime | |
} elseif type = "uncompleted" { | |
set query = "uncompletedWorkflowTasks" | |
set arg = ..UncompletedTime | |
} | |
#dim rs = $classmethod(,query _ "Func", arg) | |
while rs.%Next() { | |
set topic = rs.topic _ "." _ type | |
if prevTopic'=topic { | |
set emails = ..determineEmails(..ApprovalDomain, topic) | |
set prevTopic = topic | |
} | |
set message = rs.message | |
set:message'="" message = " - "_ message | |
set sc = ##class(Test.PPGEmail).add(..ApprovalDomain, topic, emails, rs.text _ message) | |
quit:$$$ISERR(sc) | |
} | |
quit sc | |
]]></Implementation> | |
</Method> | |
<Method name="getTime"> | |
<Description> | |
Time from which start to get messages</Description> | |
<ReturnType>%TimeStamp</ReturnType> | |
<Implementation><![CDATA[ return $g($$$EnsStaticAppData(..%ConfigName, "emailServiceTS"),"2016-10-01 00:00:00.000") | |
]]></Implementation> | |
</Method> | |
<Method name="setTime"> | |
<Description> | |
Set time from which start to get messages</Description> | |
<FormalSpec>timestamp:%TimeStamp</FormalSpec> | |
<Implementation><![CDATA[ set $$$EnsStaticAppData(..%ConfigName, "emailServiceTS") = timestamp | |
]]></Implementation> | |
</Method> | |
<Method name="sendEmails"> | |
<Description> | |
Generate mails and send to BO</Description> | |
<Implementation><![CDATA[ | |
&sql(DECLARE C1 CURSOR FOR | |
SELECT DISTINCT emails | |
INTO :email | |
FROM Test.PPGEmail_emails | |
) | |
&sql(OPEN C1) | |
&sql(FETCH C1) | |
While (SQLCODE = 0) { | |
set text = ..generateEmailText(email, ..URL) | |
set msg = ##class(Test.Alert).%New() | |
set msg.topic = "Your tasks" | |
set msg.to = email | |
set msg.text = text | |
if ..SendBO '="" { | |
do ..SendRequestAsync(..SendBO, msg ,"Email with wf tasks for: " _ email) | |
} | |
&sql(FETCH C1) | |
} | |
&sql(CLOSE C1) | |
]]></Implementation> | |
</Method> | |
<Method name="generateEmailText"> | |
<Description> | |
Generate email text with all info for user | |
do ##class(Test.EmailService).generateEmailText("[email protected]")</Description> | |
<ClassMethod>1</ClassMethod> | |
<FormalSpec>email:%String,url:%String</FormalSpec> | |
<ReturnType>%String</ReturnType> | |
<Implementation><![CDATA[ | |
set stream = ##class(%Stream.TmpCharacter).%New() | |
do stream.WriteLine("Access your tasks at: " _ url _ "<br>") | |
do stream.WriteLine("You have the foillowing tasks:<br><br>") | |
do stream.WriteLine() | |
set prevDomain = "" | |
set prevTopic = "" | |
&sql(DECLARE C2 CURSOR FOR | |
SELECT "domain", topic, text | |
INTO :domain, :topic, :text | |
FROM Test.PPGEmail | |
WHERE FOR SOME %ELEMENT(emails) (%VALUE=:email) | |
ORDER BY "domain", topic | |
) | |
&sql(OPEN C2) | |
&sql(FETCH C2) | |
While (SQLCODE = 0) { | |
if domain '= prevDomain { | |
do:prevDomain'="" stream.WriteLine("</ul>") | |
do stream.WriteLine(..writeDomain(domain)) | |
set prevDomain = domain | |
set prevTopic = "" | |
} | |
if topic '= prevTopic { | |
do:prevTopic'="" stream.WriteLine("</ul>") | |
do stream.WriteLine(..writeTopic(topic)) | |
set prevTopic = topic | |
} | |
do stream.WriteLine(..writeText(text)) | |
&sql(FETCH C2) | |
} | |
&sql(CLOSE C2) | |
do stream.WriteLine() | |
do stream.Rewind() | |
set string = stream.Read($$$MaxCacheInt) | |
return string | |
]]></Implementation> | |
</Method> | |
<Method name="writeDomain"> | |
<ClassMethod>1</ClassMethod> | |
<FormalSpec>domain:%String</FormalSpec> | |
<ReturnType>%String</ReturnType> | |
<Implementation><![CDATA[ return "<b>" _ domain _ "</b><br><br>" | |
]]></Implementation> | |
</Method> | |
<Method name="writeTopic"> | |
<ClassMethod>1</ClassMethod> | |
<FormalSpec>topic:%String</FormalSpec> | |
<ReturnType>%String</ReturnType> | |
<Implementation><![CDATA[ | |
set topic = $replace(topic, ".", ". ") | |
return "<i>" _ topic _ "</i><br><ul>" | |
]]></Implementation> | |
</Method> | |
<Method name="writeText"> | |
<ClassMethod>1</ClassMethod> | |
<FormalSpec>text:%String</FormalSpec> | |
<ReturnType>%String</ReturnType> | |
<Implementation><![CDATA[ return "<li>" _ text _ "</li>" | |
]]></Implementation> | |
</Method> | |
</Class> | |
<Class name="Test.PPGEmail"> | |
<Description> | |
Class to store subscriptions in PPG</Description> | |
<Super>%Persistent</Super> | |
<TimeChanged>64587,65337.703625</TimeChanged> | |
<TimeCreated>64266,61051.898091</TimeCreated> | |
<Property name="domain"> | |
<Description> | |
Domain</Description> | |
<Type>%String</Type> | |
<Parameter name="MAXLEN" value="100"/> | |
</Property> | |
<Property name="topic"> | |
<Description> | |
Topic</Description> | |
<Type>%String</Type> | |
<Required>1</Required> | |
<Parameter name="MAXLEN" value="1000"/> | |
</Property> | |
<Property name="emails"> | |
<Description> | |
Where to send current subscription. STORAGEDEFAULT and SQLPROJECTION allow SQL access</Description> | |
<Type>%String</Type> | |
<Collection>list</Collection> | |
<Parameter name="SQLPROJECTION" value="table/column"/> | |
<Parameter name="STORAGEDEFAULT" value="array"/> | |
</Property> | |
<UDLText name="T"> | |
<Content><![CDATA[ | |
// Not sure if it improves performance | |
]]></Content> | |
</UDLText> | |
<UDLText name="T"> | |
<Content><![CDATA[ | |
// Index emailsIndex On emails(ELEMENTS); | |
]]></Content> | |
</UDLText> | |
<Property name="text"> | |
<Description> | |
Text</Description> | |
<Type>%String</Type> | |
<Parameter name="MAXLEN"/> | |
</Property> | |
<Method name="add"> | |
<Description> | |
Add subscription | |
w ##class(Test.PPGEmail).add("APPROVAL", "Contract approval", $lb("[email protected]","[email protected]"), "text")</Description> | |
<ClassMethod>1</ClassMethod> | |
<FormalSpec>domain:%String,topic:%String,emails:%List,text:%String</FormalSpec> | |
<ReturnType>%Status</ReturnType> | |
<Implementation><![CDATA[ | |
set obj = ..%New() | |
set obj.domain = domain | |
set obj.topic = topic | |
set obj.text = text | |
if $listvalid(emails) { | |
for i=1:1:$ll(emails){ | |
do obj.emails.Insert($lg(emails,i)) | |
} | |
} | |
return obj.%Save() | |
]]></Implementation> | |
</Method> | |
<Query name="flushTable"> | |
<Type>%Query</Type> | |
<SqlQuery>SELECT * FROM Test.PPGEmail</SqlQuery> | |
</Query> | |
<Storage name="Default"> | |
<Description> | |
!!! PPG storage</Description> | |
<Type>%Library.CacheStorage</Type> | |
<DataLocation>^||Test.PPGEmailD</DataLocation> | |
<DefaultData>PPGEmailDefaultData</DefaultData> | |
<IdLocation>^||Test.PPGEmailD</IdLocation> | |
<IndexLocation>^||Test.PPGEmailI</IndexLocation> | |
<StreamLocation>^||Test.PPGEmailS</StreamLocation> | |
<Data name="PPGEmailDefaultData"> | |
<Value name="1"> | |
<Value>%%CLASSNAME</Value> | |
</Value> | |
<Value name="2"> | |
<Value>domain</Value> | |
</Value> | |
<Value name="3"> | |
<Value>topic</Value> | |
</Value> | |
<Value name="4"> | |
<Value>text</Value> | |
</Value> | |
</Data> | |
<Data name="emails"> | |
<Attribute>emails</Attribute> | |
<Structure>subnode</Structure> | |
<Subscript>"emails"</Subscript> | |
</Data> | |
</Storage> | |
</Class> | |
</Export> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment