Scott Roth · Apr 10, 2018 go to post

Just adding a bump to see if anyone has a way to have the system users bypass the delegated sign on without having to hard code the system.

THanks

Scott

Scott Roth · Apr 6, 2018 go to post

Using the Ensemble Scheduler we ran into issues when we had multiple schedules running at the same time, and notice it pausing the whole environment just to refresh. That's why we went down this route.

It looks like this particular Inbound.SQL.Adapter service that caused the issue had a data lock on the database which caused more issues. So I am wondering if the data lock caused EnsConfigItem() to get in a hung status.

Scott Roth · Mar 15, 2018 go to post

I have always called it the other way around with the outbound before the I in the Execute Procedure Parm Array.

Method InsertProviderDivisionSp(pRequest As osuwmc.CPD.DataStructures.InsertProviderDivision, Output pResponse As Ens.Response) As %Status
{
set SPQuery = "{ ?= call usp_Interfaces_Insert_ProviderDivision_Ens(?,?,?,?) }"
set par = 4
set par(1) = pRequest.DoctorNumber
set par(2) = pRequest.Division
set par(3) = pRequest.UpdatedBy
set par(4) = pRequest.OrderBy
set tSC = ..Adapter.ExecuteProcedureParmArray(.InsertDivision,.outputs,SPQuery,"oiiii",.par)
if 'tSC write " failed ",tSC 
quit tSC
}
 


or you can call it like...

Method InsertProviderPreference(pRequest As osuwmc.CPD.DataStructures.InsertPreferences, Output pResponse As Ens.Response) As %Status
{
set SPQuery = "{ ?= call usp_Interfaces_Insert_ProviderPreference(?,?,?,?,?) }"
set parm = 6
set parm(1,"SqlType")=$$$SQLVARCHAR
set parm(1,"IOTypes")=$$$SQLPARAMOUTPUT
set parm(2) = pRequest.DoctorNumber
set parm(2,"SqlType")=$$$SQLVARCHAR
set parm(2,"IOTypes")=$$$SQLPARAMINPUT
set parm(3) = pRequest.Preference
set parm(3,"SqlType")=$$$SQLNUMERIC
set parm(3,"IOTypes")=$$$SQLPARAMINPUT
set parm(4) = pRequest.PreferenceValue
set parm(4,"SqlType")=$$$SQLNUMERIC
set parm(4,"IOTypes")=$$$SQLPARAMINPUT
set parm(5) = pRequest.PreferenceDesc
set parm(5,"SqlType")=$$$SQLVARCHAR
set parm(5,"IOTypes")=$$$SQLPARAMINPUT
set parm(6) = pRequest.UpdatedBy
set parm(6,"SqlType")=$$$SQLVARCHAR
set parm(6,"IOTypes")=$$$SQLPARAMINPUT
set tSC = ..Adapter.ExecuteProcedureParmArray(.InsertPreference,.outputs,SPQuery,"oiiiii",.parm)
if 'tSC write "Failed",tSC
quit tSC
}

Scott Roth · Mar 14, 2018 go to post

Thanks not the exact answer that I was looking for but I think I can make it work somehow.

Scott Roth · Mar 12, 2018 go to post

What does your... ##class(SamsReq).%New() look like? Here is what my Request and code look like as a whole. I have used this many times across many services in different fashions. I am using JDBC vs ODBC which maybe a difference, not sure.


Class osuwmc.CPD.DataStructures.StartJobRequest Extends (%Library.Persistent, %XML.Adaptor) [ Not ProcedureBlock, SqlRowIdPrivate ]
{
Property StartJobStatus As %Integer;
Storage Default
{
<Data name="StartJobRequestDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>StartJobStatus</Value>
</Value>
</Data>
<DataLocation>^osuwmc.CPD59D.StartJobReqE986D</DataLocation>
<DefaultData>StartJobRequestDefaultData</DefaultData>
<IdLocation>^osuwmc.CPD59D.StartJobReqE986D</IdLocation>
<IndexLocation>^osuwmc.CPD59D.StartJobReqE986I</IndexLocation>
<StreamLocation>^osuwmc.CPD59D.StartJobReqE986S</StreamLocation>
<Type>%Library.CacheStorage</Type>
}
}


Class osuwmc.CPD.UpdateClarityAddressesFromCPDService Extends Ens.BusinessService [ ClassType = "", ProcedureBlock ]
{
Parameter ADAPTER = "EnsLib.SQL.InboundAdapter";
Parameter REQUESTCLASSES As %String = "EnsLib.SQL.Snapshot";
Property InitDSN As %String;
Method OnInit() As %Status
{
Set ..InitDSN = ..Adapter.DSN
//Set ..Adapter.ConnectAttrs = "QueryTimeout:45" ; try this too just in case...
Quit $$$OK
}
Method OnProcessInput(pInput As EnsLib.SQL.Snapshot, pOutput As %RegisteredObject) As %Status
{
set req=##class(osuwmc.CPD.DataStructures.StartJobRequest).%New()
set req.StartJobStatus = pInput.Get("1")
set sc = ..SendRequestSync("CPDClarityAddressUpdateBPL",req,.pOutput)
    Quit sc
}
}
 

Scott Roth · Mar 12, 2018 go to post

I have used this in the past...

Under my Data Settings I use the following as my Query to act like a status kick off for my job...

SELECT  1

Then my service does...

Method OnProcessInput(pInput As EnsLib.SQL.Snapshot, pOutput As %RegisteredObject) As %Status
{
     set req=##class(osuwmc.CPD.DataStructures.StartJobRequest).%New()
     set req.StartJobStatus = pInput.Get("1")
     set sc = ..SendRequestSync("CPDClarityAddressUpdateBPL",req,.pOutput)
    Quit sc
}

It appears you are not setting your request variable so there is nothing to send to your DQTT.

Scott Roth · Mar 9, 2018 go to post

Can I still add code in Business Studio and it maintain integrity to be able to still open in the BPL editor?

I added the following in my while loop, but then the BPL hung.

<assign property='..%Process.%SessionId' value='""'/>
Scott Roth · Mar 1, 2018 go to post

Instead of storing it at the Credentials level, I have created a Global to store the LDAP server information. However when I am calling $Get(^OSUMCLDAP(Server)) nothing is being returned. Am I not calling it appropriately? Do I need to add an include statement somewhere to make sure it includes the globals?

#define LDAPServer $Get(^OSUMCLDAP(Server))

 $$$WindowsLDAPServer{
AdminDN=$Get(^OSUMCLDAP(User))
AdminPW=$Get(^OSUMCLDAP(Pass))
 }

Thanks

Scott

Scott Roth · Feb 26, 2018 go to post

Sorry I am learning AD/LDAP as I go. I talked with the "powers that be" and they did confirm that I do not see a separate username and password to BIND to LDAP, and that I can use the UserName. So I will make that switch in the code above.

Thanks everyone.

Scott Roth · Feb 26, 2018 go to post
  • To verify against LDAP we needed a separate user other than the user trying to log into the system.
  • Also to make it easier on all parties involved instead of using new Attributes in LDAP, I had a group created that I could match up against a role of the same name in Ensemble.

So if I have an additional user I need to use to verify as the LDAP search user, how would I store the user name and password in this scenario?

Scott Roth · Feb 26, 2018 go to post
ZAUTHENTICATE(ServiceName,Namespace,Username,Password,Credentials,Properties) PUBLIC {
#include %occErrors
#include %sySecurity
#include %syLDAP

 
 s LDAPServer="xxxxx.xxxxxx.xxxx"
 s LD=##Class(%SYS.LDAP).Init(LDAPServer)
 i LD=0 {
   s Status=##Class(%SYS.LDAP).GetLastError()
   //g Error
 }
 s LDAPUser = "ensemble.Services"
 s LDAPPass = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"
 
 s Status=##Class(%SYS.LDAP).Binds(LD,"",$lb(LDAPUser,"",LDAPPass),$$$LDAPAUTHNEGOTIATE)
 i Status'=$$$LDAPSUCCESS q $SYSTEM.Status.Error($$$InvalidUsernameOrPassword)
 
  s BaseDN="dc=OSUMC,dc=EDU"
 s Filter="sAMAccountname="_Username
 s Attributes=$lb("sAMAccountname","displayName","mail")
 s Status=##Class(%SYS.LDAP).SearchExts(LD,BaseDN,$$$LDAPSCOPESUBTREE,Filter,Attributes,0,"","",10,1,.SearchResult)
 i Status'=$$$LDAPSUCCESS {
 w !,"SearchExts error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
 g Done
 }
 s CurrentEntry=##Class(%SYS.LDAP).FirstEntry(LD,SearchResult)
 i CurrentEntry=0 {
 s Status=##Class(%SYS.LDAP).GetError(LD)
 w !,"FirstEntry error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status) 
 g Done
 }
 s DN=##Class(%SYS.LDAP).GetDN(LD,CurrentEntry) 
 s Attribute=##Class(%SYS.LDAP).FirstAttribute(LD,CurrentEntry,.Ptr)
 while (Attribute'="") {
   s Values=##Class(%SYS.LDAP).GetValuesLen(LD,CurrentEntry,Attribute)
   //w Values
   s Properties("Attributes",Attribute)=Values
   //w Attributes(Attribute)
  s Attribute=##Class(%SYS.LDAP).NextAttribute(LD,CurrentEntry,.Ptr)
 }
 s Properties("Username")=$li(Properties("Attributes","sAMAccountName"))
 k Properties("Attributes","sAMAccountName")
 s Properties("FullName")=$li(Properties("Attributes","displayName"))
 k Properties("Attributes","displayName")

 s GroupFilter="(&(objectClass=group)(member:1.2.840.113556.1.4.1941:="_DN_"))"
 s GroupAttributes=""
 s Status=##Class(%SYS.LDAP).SearchExts(LD,BaseDN,$$$LDAPSCOPESUBTREE,GroupFilter,GroupAttributes,0,"","",10,0,.GroupSearchResult)
 i Status'=$$$LDAPSUCCESS {
w !,"SearchExts error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
g Done
 }
 s GroupNumEntries=##Class(%SYS.LDAP).CountEntries(LD,GroupSearchResult)
 i GroupNumEntries=-1 {
s Status=##Class(%SYS.LDAP).GetError(LD)
//w !,"CountEntries Group error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
g Done
 }

 w !
 i GroupNumEntries=0 {
w !,"No nested groups for "_Username_" found"
g Done
 }
 i GroupNumEntries>0 {
//w !,"Found "_GroupNumEntries_" nested groups for user "_Username
 }

#;Get the dn of the first entry returned.
 s GroupCurrentEntry=##Class(%SYS.LDAP).FirstEntry(LD,GroupSearchResult)
 i GroupCurrentEntry=0 {
s Status=##Class(%SYS.LDAP).GetError(LD)
w !,"FirstEntry error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
g Done
 }
 s Groups=""
 While (GroupCurrentEntry'=0) {
s GroupDN=##Class(%SYS.LDAP).GetDN(LD,GroupCurrentEntry)
i GroupDN="" {
s Status=##Class(%SYS.LDAP).GetError(LD)
w !,"GetDN Group error: "_Status_" - "_##Class(%SYS.LDAP).Err2String(Status)
q
}
s CN=$p(GroupDN,",",1)
s AD=$p(CN,"=",2)
s AD=$zcvt(AD,"L") 
set exists=''$d(^|"%SYS"|SYS("Security","RolesD",AD))
if exists{
s Properties("Roles") = AD
w Properties("Roles")
}
#;Save for later display
//w !,AD
  s GroupCurrentEntry=##Class(%SYS.LDAP).NextEntry(LD,GroupCurrentEntry)
 }
Done
 i $d(SearchResult) d ##Class(%SYS.LDAP).MsgFree(SearchResult)
 i $d(GroupSearchResult) d ##Class(%SYS.LDAP).MsgFree(GroupSearchResult)
#;Close the connection and free the LDAP in memory structures.
 i $d(LD) d ##Class(%SYS.LDAP).UnBinds(LD)
 q $SYSTEM.Status.OK()
Error s $zt=""
 w !,"Cache error: "_$ze
 g Done
}
Scott Roth · Feb 19, 2018 go to post

When I run...

 i=2:1:$LL(list2) write !,i,"",$p($li(list2,i),",")
 

I am getting the output of

2Access.Ensemble.Developer.User2

How can I remove the 2 from the beginning and ending of the string, then compare it to the %SYS Roles?

Thanks

Scott

Scott Roth · Feb 19, 2018 go to post

I believe it is either a String for a Fixed Sequence. I cut and pasted that from a document given to me to show me the LDAP/AD Attributes. I believe the structure is {CN=  ,OU=,  DC=, DC= , CN=,OSU=,DC=,DC=.....}  so every time we see a CN its a new group which is what I want to key off of.

Scott Roth · Feb 19, 2018 go to post

You can use a Data Lookup table to specify the Subject based on the Error you are looking for.

Scott Roth · Feb 19, 2018 go to post

So I am trying to take a list of....

managedObjects                       : {CN=Access.Ensemble.Developer.User,OU=Access Groups,DC=OSUMC,DC=EDU, CN=[CPD Admin],OU=Distribution Lists,DC=OSUMC,DC=EDU, CN=[MUSE_Access],OU=Distribution Lists,DC=OSUMC,DC=EDU, CN=[IT eMaterials],OU=Distribution Lists,DC=OSUMC,DC=EDU...}

How can I put this into a list and properly filter out the values I need. For Example Access.Ensemble.Developer.User is my end target that corresponds to a role of that same name.

I am struggling with how to appropriately pull this information out so I can do the IF statements below.

Scott Roth · Feb 16, 2018 go to post

I have run into a case where the Last Name or First Name contains a Hyphen but no spaces between the Hyphen and the split name. I have tried "S", but that seemed not to help.

Anyone have suggestions on how to handle names with a hyphen besides doing an IF statement, and Piecing the Name apart to make it Proper Case?

Scott Roth · Feb 15, 2018 go to post

Do you have access via Powershell? I was given some AD commands to pull using Powershell. I used this information to make sure the AD Group was being set correctly, and to figure out what to look for in my ZAUTHENTICATE that I am trying to build.

Scott Roth · Feb 9, 2018 go to post

Thanks did not realize that Cache had a function built in for that.

Scott Roth · Feb 7, 2018 go to post

The application is sending us the following back... Of course it does not match up to any structure but like you said they probably have omitted the backslash from the ACK causing the error.

MSH|^~\&|GCRC|RDM|INTENG||20180207084732||SIU^S12|70577207|P|2.3|
MSA|AA|Successful|

I went ahead and made copies of 4 different EnsLib.HL7 classes, and modified the Parser to not throw the warning message. So for it is working like it should.

Scott Roth · Feb 6, 2018 go to post

This was a custom interface written some time ago, and only maintained by hiring someone outside to update the contents of the software.

We are getting the following warning message "Discarding received non-HL7 data(1) 'r'" . We have tried different framing values to no avail, and it still wants to log this warning. We were just looking for a way to shut this warning off for just this one Operation.

Scott Roth · Feb 5, 2018 go to post

I have found that the text of "Discarding Received non HL7 data" lives in the EnsLib.HL7.Parser.cls. Has anyone tired to copy and make their own HL7.Parser that would not throw this warning message? It seems pretty simple but I am having issues trying to call my updated Parser.cls. All I want to do it comment this error out so it is not thrown and taking up database space.

 

Thanks

Scott

Scott Roth · Jan 24, 2018 go to post

When calling a SQL call, I normally return the value in a Snapshot. 

You can do this 1 of two ways...

1. Set two variables....One for the Outbound HL7 Message, and one for the structure of your snapshot. Set your source equal to your snapshot variable and your target being the Outbound HL7 context variable. Use a Data transformation to take the information from the Snapshot and insert it into the HL7.

2. Create a variable for your ID variable, and loop through the Snapshot to set that ID variable. Then create a dummy data transformation that you can use to set your HL7 field to your context.ID variable.

I have plenty of examples of doing this in either way. There might be others that have a better solution, its just what I have used in the past.

Scott Roth

The Ohio State University Wexner Medical Center

Scott Roth · Jan 12, 2018 go to post

No luck but thanks.

I took the query from $$$LOGINFO(select)  and executed it fine in SQL Server. It showed Ensemble should of returned 185 rows.

For every database pull Ensemble stores either that whole message or a key value so it knows that record has already been read.  I can't figure out how to get around that when I do a ExecuteQuery() call inside my Operation.

Scott Roth · Nov 3, 2017 go to post

Simplifying the query to just the select statement worked, however is there a good way to convert date/time from GMT to EST?