Is the problematic class particularly large?
- Log in to post comments
Is the problematic class particularly large?
Since you mention Clinical Viewer I assume you're talking about HealthShare and not about TrakCare. HealthShare includes documentation on customizing the Clinical Viewer here:
https://docs.intersystems.com/hs20211/csp/docbook/Doc.View.cls?KEY=HECUI
Note that you'll need to be a HealthShare customer to access this documentation.
I agree with Vic that this is probably not worth the complexity. The rule definitions are stored as XML -- to change them you'll need to fetch the XML, modify it, then save the changed XML and re-compile the class. It would look something like this:
set tMyRuleClassName="My.RoutingRuleClass"
&SQL(select %id into :tXDataID from %Dictionary.XDataDefinition where parent = :tMyRuleClassName and Name='RuleDefinition')
if (SQLCODE > 0) {
// Query returned no results. Do some error handling
quit
}
set tRuleDefinition=##class(%Dictionary.XDataDefinition).%OpenId(tXDataID)
/* This stream contains the raw XML of the RuleDefinition. It looks like this:
<ruleDefinition alias="" context="EnsLib.HL7.MsgRouter.RoutingEngine" production="TESTINGPKG.FoundationProduction">
<ruleSet name="" effectiveBegin="" effectiveEnd="">
<rule name="">
<when condition="1">
<send transform="" target="HL7.File.Out"/>
<return/>
</when>
</rule>
</ruleSet>
</ruleDefinition>
*/
set tRuleDefinitionXMLStream=##class(%Stream.GlobalCharacter).%New()
do tRuleDefinitionXMLStream.CopyFrom(tRuleDefinition.Data)
// Use XML classes to manipulate the stream
// (docs about XML classes: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GXML_intro)
// ...
// ...
// ...
// Ok, done making changes to the stream
// Copy the changed XML back into the RuleDefinition
do tRuleDefinition.Data.CopyFrom(tRuleDefinitionXMLStream)
// Save the changees to the rule definition XData
set tSC=tRuleDefinition.%Save()
// Check if tSC indicates an error and do something about it
if $$$ISERR(tSC) {
// Do some error handling
}
// Recompile the rule class
set tSC=$System.OBJ.Compile(tMyRuleClassName,"cuk",.errorlog,1)
// Check if tSC indicates an error and do something about it
if $$$ISERR(tSC) {
// Do some error handling
}I'm not aware of third-party widgets for ZEN Reports -- maybe someone else knows more.
Something else you could explore is generating SVG code to draw the graphic and output the SVG directly into an xsl-fo element using Zen Reports' <write>.
To get the gradient, you could assign a background image to a <container>.
You can definitely do the OAuth requests using basic HTTP calls.
Doing it in OnInit won't work because the token you receive has an expiration time.
You'll need to:
- For every message your Operation receives you'll need to check if the cached token's expiration time has passed (or if there's no cached token).
- If you need a new token, do your POST call to get a new token and cache it.
You'll probably want to check with the vendor to confirm where they expect to see it. One possibility is to pass it to GetAccessTokenClient as a property.
https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View…
Also, this sample assumes that they're using the client credentials grant type. This is the most likely option, but it would be worth confirming.
I like to use local multidimensional arrays as Dmitriy suggested.
Also, array objects are key/value based -- iterating through the keys always occurs in sorted order.
Here's a cheat sheet I put together on creating an OAuth client definition:
Go to System Administration >> Security >> OAuth 2.0 >> Client
Choose “Create Server Description”
Enter the endpoint URL provided for the OAuth server and select the TLS config you created for the OAuth server. Click “Discover and Save”. It should populate details about the OAuth server.
.png)
After saving, a new server entry will appear. Click “Client Configurations” and then “Create Client Configuration”
.png)
Enter the name details, select the TLS config, and choose Client Credentials as the grant type. The redirect URL won’t actually be used, but you’ll need to enter something for hostname anyway.
Keep track of what you enter for “Application Name”. You’ll need to put this in your custom code.
.png)
Switch to the “Client Credentials” tab and enter the Client ID and Client Secret you were given.
.png)
I think for Issuer endpoint you need to include the protocol:
This snippet uses the built-in OAuth client to request a token and then add it to the HTTP header.
You'll need to configure an entry for the OAuth server with a sub-entry for this specific client:
https://docs.intersystems.com/healthconnectlatest/csp/docbook/Doc.View…
In the case of the sample code below, the client I configured is named TestClientA.
set isAuth=##class(%SYS.OAuth2.AccessToken).IsAuthorized("TestClientA",,"*",.accessToken,.idtoken,.responseProperties,.error)
$$$TRACE("isAuth:"_isAuth)
if 'isAuth {
set tSC=##class(%SYS.OAuth2.Authorization).GetAccessTokenClient("TestClientA", "*",, .error)
set isAuth=##class(%SYS.OAuth2.AccessToken).IsAuthorized("TestClientA",,"*",.accessToken,.idtoken,.responseProperties,.error)
$$$TRACE("isAuth2:"_isAuth)
if 'isAuth {
quit $$$ERROR(5001,"Problem authenticating")
}
}
$$$TRACE("access_token: "_accessToken)
$$$TRACE("expires_in: "_responseProperties("expires_in"))
$$$TRACE("scope: "_responseProperties("scope"))
$$$TRACE("token_type: "_responseProperties("token_type"))
s tSC=pRequest.HTTPHeaders.SetAt("Bearer "_accessToken,"Authorization")In your REST operation you can just do this. In this case the header name is "Authorization" but you can change it to whatever is required.
s tSC=pRequest.HTTPHeaders.SetAt(tAuthInfo,"Authorization")
You need to instantiate that class as an object and then you can call methods on the object:
https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View…
Please post the source code for ITB.HL7.BS.XMLService so we can take a look.
One thing to check: is GetAck declared as a "Method" or as a "ClassMethod"? If it is declared as a "Method" then that is the problem. The syntax you're using to call it is class method syntax, not instance method syntax.
If you're working with an Ensemble SQL operation, then pResultSet is an EnsLib.SQL.GatewayResultSet object. Have a look at the HasDataStream and GetDataStream methods.
You can use TCP adapters for this.
Here's a working example based on Craig's original sample. It sends a request with 3 mime parts: a form variable with a value, an XML file, and a PDF file loaded from disk.
set tURL="http://some.url/path/to/upload"
set tHttpRequest = ##class(%Net.HttpRequest).%New()
// ----------------------------------------
// Instantiate reportId MIME Part
Set reportId = ##class(%Net.MIMEPart).%New()
// Define/Set the Content-Disposition header indicating how this MIME part is encoded and what it contains.
// Final string looks like: form-data; name="reportId"
S tContentDisp = "form-data; name="_$CHAR(34)_"reportId"_$CHAR(34)
Do reportId.SetHeader("Content-Disposition", tContentDisp)
// Write the reportId to the MIME Part body.
Set reportId.Body = ##class(%GlobalCharacterStream).%New()
Do reportId.Body.Write("RptID12345")
// ----------------------------------------
// Instantiate file1 (XML Doc) MIME Part
Set file1 = ##class(%Net.MIMEPart).%New()
// Define/Set the Content-Disposition header indicating how this MIME part is encoded and what it contains.
// Final string looks like: form-data; name="file1"; filename="<pRequest.CaseNumber>.xml"
S tContentDisp = "form-data; name="_$CHAR(34)_"file1"_$CHAR(34)_"; filename="_$CHAR(34)_"xmlfile.xml"_$CHAR(34)
Do file1.SetHeader("Content-Disposition", tContentDisp)
// Write XML to the MIME Part body.
Set file1.Body = ##class(%GlobalCharacterStream).%New()
Set file1.ContentType = "application/xml"
do file1.Body.Write("<myXML><element1>value</element1></myXML>")
// ----------------------------------------
// Instantiate file1 (PDF Report) MIME Part
Set file2 = ##class(%Net.MIMEPart).%New()
// Define/Set the Content-Disposition header indicating how this MIME part is encoded and what it contains.
// Final string looks like: form-data; name="file1"; filename="<pRequest.CaseNumber>.xml"
S tContentDisp = "form-data; name="_$CHAR(34)_"file2"_$CHAR(34)_"; filename="_$CHAR(34)_"PDFFile.pdf"_$CHAR(34)
Do file2.SetHeader("Content-Disposition", tContentDisp)
// Get the content for the PDF file
set tFile=##class(%Stream.FileBinary).%New()
do tFile.LinkToFile("C:\Projects\test.pdf")
// Write PDF content to the MIME Part body.
Set file2.Body = ##class(%GlobalBinaryStream).%New()
Set file2.ContentType = "application/pdf"
do file2.Body.CopyFrom(tFile)
// ----------------------------------------
// Pack everything up and send the request
// Package sub-MIME Parts into Root MIME Part
Set rootMIME = ##class(%Net.MIMEPart).%New()
Do rootMIME.Parts.Insert(reportId)
Do rootMIME.Parts.Insert(file1)
Do rootMIME.Parts.Insert(file2)
// Write out Root MIME Element (containing sub-MIME parts) to HTTP Request Body.
Set writer = ##class(%Net.MIMEWriter).%New()
Set sc = writer.OutputToStream(tHttpRequest.EntityBody)
if $$$ISERR(sc) {do $SYSTEM.Status.DisplayError(sc) Quit}
Set sc = writer.WriteMIMEBody(rootMIME)
if $$$ISERR(sc) {do $SYSTEM.Status.DisplayError(sc) Quit}
// Specify the Content-Type and Root MIME Part Boundary (required for multipart/form-data encoding.)
Set tContentType = "multipart/form-data; boundary="_rootMIME.Boundary
Set tSC = tHttpRequest.SetHeader("Content-Type",tContentType)
// Call SendFormDataArray method in the adapter to execute POST. Response contained in tHttpResponse
Set tSC=tHttpRequest.Post(tURL)
If $$$ISERR(tSC) {
// Oops, an error. Do something
}1) Ah, that makes sense. SendFormDataArray is a method in EnsLib.HTTP.OutboundAdapter, but you're not using an adapter.
To do a POST using %Net.HTTPRequest, you'll need to use the Post() method.
2) I had a look at the HTTP specs, and it looks like content-disposition is required for each part:
multipart/form-data body, the HTTP Content-Disposition general header is a header that must be used on each subpart of a multipart body to give information about the field it applies to. The subpart is delimited by the boundary defined in the Content-Type header. Used on the body itself, Content-Disposition has no effect.You can set this like any other header using the SetHeader method in %Net.MIMEPart. You're already doing that here:
Do BinaryMIMEPart.SetHeader("Content-Type", ContentType)You can use %Stream.FileBinary to create a file-based stream and then copy the contents from the %CSP.BinaryStream to it.
Set stream=##class(%Stream.FileBinary).%New()
Set sc=stream.LinkToFile("c:\myfile.txt")
Set sc=stream.CopyFromAndSave(myCSPBinaryStream)What error did you receive when you used SendFormDataArray?
I noticed that retrievePerson doesn't instantiate pOutput.
Yone, try removing this line and test if it works:
Set pOutput=##class(%GlobalBinaryStream).%New()If you are sending a body in your request to the REST service then it needs to be a POST, not a GET.
<Route Url="/consultarImagen" Method="GET" Call="consultarImagen"/>
There is an included utility that lets you see at a glance which ports are configured for use by which business services. Naturally this won't tell you if some other OS application is using a port - for that you'd use netstat as mentioned above.
https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.U…
Emanuel,
Since you're writing this in a normal .int, you won't want to use "..%HTTPRequest". You can just instantiate a new request object and name it something like httpRequest:
set httpRequest=##class(%Net.HttpRequest).%New()
And instead of Craig's ..Adapter call, you can use httpRequest's Post method.
Joey, this does what Nicole described above. If any of the OBX:3.1 values match WBC, the message will be sent to the business operation.
.png)
Sample code using Embedded SQL:
set tName="EMRNameA"
set tEMRNameLike="%"_tName
&SQL(SELECT DataValue INTO :tValue FROM Ens_Util.LookupTable WHERE TableName = 'EMRName' AND KeyName LIKE :tEMRNameLike)
if (SQLCODE = 0) {
write "tValue:",tValue,!
} else {
write tName_" Not found",!
}Have you considered using SQL for this rather than going directly to the globals?
SELECT DataValue FROM Ens_Util.LookupTable WHERE TableName = 'EMRName' AND KeyName LIKE '%EMRNameA'