Stop your HS instance, then:
- Log in to post comments
Stop your HS instance, then:
Every routine, class or global that start with "%" is mapped to a system database, usually "%SYS".
In your case %Test.mac is (by default) mapped to the %SYS database.
It seems that you (the user you connect to IRIS) don't have permissions to write to the %SYS database.
Please note that during IRIS upgrade all routines starting with "%" are DELETED, unless they start with %z or %Z, so I suggest to use a different name or, better, create your code in other namespace/database with consistent naming (package name) and map it from your application namespaces. If you need your code in all namespaces, create a mapping for the %ALL (pseudo) namespace.
Hi Daniel 😊
can you elaborate "$p($view(-1,-3),"^",6)"? 😉
Your code gives the impression that to implement this solution requires knowledge that we "simple humans" don't have.
Fortunately this is not the case, instead of the cryptic, obscure, arcane and undocumented $p($view(-1,-3),"^",6) the simple $ZNAME special variable can be used. 😃
I'd implement a custom function, create a class like:
Class Community.CustomFunctions Extends Ens.Rule.FunctionSet
{
/// Returns Age in years from DOB in YYYYMMDD formatClassMethod GetAge(DateOfBirth As%String) As%Integer
{
Quit (($H-$ZDATETIMEH(DateOfBirth,8)) \ 365.25)
}
/// Returns Age in days from DOB in YYYYMMDD formatClassMethod GetAgeDays(DateOfBirth As%String) As%Integer
{
Quit ($H-$ZDATETIMEH(DateOfBirth,8))
}
}
Then from any Rule or DTL transformation you can use these two function as any other built in function.
Make sure DOB is not null ("") before calling the function.
If you tell us what format you use for date of birth, then we can give you the code you may use
You cannot assign to arbitrary variable in rules. (edited, in fact, you can see other posts! 🙂)
To store some temporary data you can assign it to aux.RuleActionUserData
In case you want only the portalUrl and pureID of the first element of "item" array, then all you need is:
Write responseData.items.%Get(0).portalUrl
Write responseData.items.%Get(0).pureID
You case is simpler, you do know the json structure and all you need is to iterate the "items" array and find all the portalUrl and pureID properties. Note that since there can be many "items", there may be many portalUrl and pureID.
Set itr=responseData.items.%GetIterator()
while itr.%GetNext(.key, .value) {
Write value.portalUrl,!
Write value.pureID,!
}
P.S.: it's unlikely that the Response you get from a REST call is a %DynamicAbstractObject since, by definition, that's an abstract class and cannot be instantiated, what you actually get is a subclass of %DynamicAbstractObject, a %DynamicObject in this case or a %DynamicArray when the response is an array.
%Stream.DynamicBinary it's a stream that use a different storage, if you need your data in a %Stream.TmpCharacter just copy the stream using CopyFrom() method.
Both %Stream.DynamicBinary and %Stream.TmpCharacter are %StreamObject descendant, the use/have the same interface (methods) but a different storage.
If you receive "special characters, such as non-utf-8 characters either control characters or unicode characters" it means that somewhere upstream character set conversion is not properly configured/handled.
I'm not sure that removing characters from a text is a proper solution, instead I'd fix the problem from the source identifying where the character set conversion is not properly configured/handled and fixing it.
Surely after removing some characters the text can be printed and parsed by downstream systems, but....it's going to be a different text, potentially with a different meaning!
You did not provide details, so I try to guess.
You are writing a Business Operation that use the EnsLib.HTTP.OutboundAdapter, if so, double check the signature of the method SendFormDataArray() that is:
Method SendFormDataArray(Output pHttpResponse As %Net.HttpResponse, pOp As %String, pHttpRequestIn As %Net.HttpRequest, pFormVarNames As %String = "", ByRef pData, pURL As %String) As %Status
Your call is:
set tSC = ..Adapter.SendFormDataArray(.tHTTPResponse, "POST", tHTTPRequest, tURL, tPayload)
I think the passed arguments don't match the method signature.
Is it very complex to develop a new DICOM adapter that will use global streams instead?
It should not for InterSystems.
Note that the DICOM adapter/implementation in IRIS use some external ISC undocumented libraries.
It depends on use case.
In my case we use DICOM integration to orchestrate archiving of studies between AETs, so we use commands like CFIND, CMOVE etc. and never download a study.
At the moment we have 290K+ files, the biggest is 1KB.
I think that even when you download studies having the option to use global streams instead of files would be extremely useful in a mirror configuration.
Depending on the environment, having an high availability single shared directory between mirror members can be expensive from MANY point of view (resources, configuration, maintenance, backup, security, etc.).
When an async DR is in place things get even more complicated.
As it stands, using IRIS DICOM interoperability in mirror configuration it's a nightmare.
But...this is supposed to be a High Availability environment/configuration, a NAS would add a single point of failure along with the network between IRIS and NAS, there are also performance and maybe security consideration IMHO.
You edited your post after my answer 😊
Am I missing something or canonicalization does not minify the XML?
For other reasons (how data is consumed) we cannot compress it and the target property is a %String.
Maybe creating a compressed string datatype can be another option in other situations but in this case the target property/class is part of HealthShare (a Registry Slot).
Have you tried? Can you share your code?
What date 00010101 represent?!
To me looks like a wrong date......how do you want to handle it??
Yes, it's possible to call routines or methods from Java using the Native API for Java, I suggest to start with the documentation:
Configure a VIP for the mirror using built in IRIS VIP configuration, or, if you cannot use built in VIP, you can use an external balancer (F5, HAProxy etc.) to handle/create the VIP "externally".
Then instead of using the IP/name of the mirror members you use the VIP address/name to connect to the active mirror member node. Use VIP for ODBC and any other connection to IRIS.
You did not specified what kind of HealthShare environment you have in place (UCR?), depending of your HealthShare setup a VIP is a prerequisite for configuring an HealthShare federation.
If during installation your code "knows" what namespace is used, then you can get the associated resources as described in my previous post.
If you don't know the namespace, then...game over, no way.
Maybe you can run a setup script after install that add to your role the required resources?
Yes, it's normal, Web Applications are local configuration on each mirror member.
If/when you create a new Web Application you need to create it in all mirror members. Personally I prefer to create the application using a script (a class method) that is deployed and run (typically along with code etc.) in all members nodes.
In addition to Web Applications note that also all security related configuration (Users, Roles, Resources, SQL privileges etc.) are local to each mirror member.
It's my understanding that InterSystems is working on enhancing the mirror to include security configuration synchronization (Web Applications are part of the Security) between mirror member.
To address similar cases some time ago I developed a little utility to export to a %DynamicArray the output from an SQL query.
In my case I had to export existing classes that did not extend %JSON.Adaptor (I'm not even sure %JSON.Adaptor existed at that time).
Class Community.SQL2JSON
{
/// Execute am SQL query and returns a %DynamicArray containing the rows (as %DynamocObject) returned by the query/// RetDynArray is the returned %DynamicArray containing the results/// Parameters used by the query must be included in the query using placeholders (?) e passed By Reference in the local variable array ParamArray/// where the root node contains the number of parameters, ie:/// ParamArray = 1/// ParamArray(1) = 123ClassMethod QueryToJSON(ByRef SQLQuery As%String, Output RetDynArray As%DynamicArray, ByRef ParamArray As%String) As%Status
{
Set sc=$$$OKTry {
Set stSql=##class(%SQL.Statement).%New()
Set stSql.%ObjectSelectMode=0Set rsSql=##class(%SQL.Statement).%ExecDirect(.stSql, .SQLQuery,ParamArray...)
If rsSql.%SQLCODE < 0 {
Set sc = $$$ERROR($$$GeneralError,"%SQLCODE="_rsSql.%SQLCODE_", %Message="_rsSql.%Message)
Quit
}
If '$IsObject($g(RetDynArray)) Set RetDynArray = []
While rsSql.%Next(.sc) {
If$$$ISERR(sc) QuitSet RowDynObj={}
Set sc=..RowToDynObj(stSql,rsSql,.RowDynObj)
If$$$ISERR(sc) QuitDo RetDynArray.%Push(RowDynObj)
}
} Catch CatchError {
#dim CatchError as%Exception.SystemExceptionSet sc=CatchError.AsStatus()
}
Quit sc
}
/// Convert a recordset in a %DynamicObject with property name equal to the column name/// If RowDynObj is passed, then adds the properties are added to it, otherwise creates and returns a new dynamic objectClassMethod RowToDynObj(StSql As%SQL.Statement, RsSql As%SQL.StatementResult, ByRef RowDynObj As%DynamicObject) As%Status
{
Set sc=$$$OKTry {
If '$IsObject($g(RowDynObj)) Set RowDynObj= {}
For col=1:1:StSql.%Metadata.columnCount {
Set ColumnName=StSql.%Metadata.columns.GetAt(col).colName
Set ColumnValue=$Property(RsSql,ColumnName)
Do RowDynObj.%Set(ColumnName,ColumnValue)
}
} Catch CatchError {
#dim CatchError as%Exception.SystemExceptionSet sc=CatchError.AsStatus()
}
Quit sc
}
}
Using it is very simple:
EPTEST>Set SQLQuery="select Name, DOB as ""Birth Date"", Home_City as City from Sample.Person where Home_City=?"
EPTEST>Set ParamArray=1
EPTEST>Set ParamArray(1)="Newton"
EPTEST>Set sc=##class(Community.SQL2JSON).QueryToJSON(SQLQuery,.RetDynArray,.ParamArray)
EPTEST>Do RetDynArray.%ToJSON()
[{"Name":"Uhles,Susan D.","Birth Date":31836,"City":"Newton"},{"Name":"Ubertini,Debby N.","Birth Date":42513,"City":"Newton"},{"Name":"Harrison,Rob E.","Birth Date":62265,"City":"Newton"},{"Name":"Adams,Robert E.","Birth Date":62769,"City":"Newton"}]
EPTEST>"But the app could be installed in any database, right?"
I believe it's wrong, the app could be installed in any NAMESPACE.
Now the question is, what role have access to the databases associated with the namespace?
Leaving mappings aside, a namespace uses two databases, "Default Database for Globals" and "Default Database for Routines" (code), usually the two databases coincide but you cannot assume it's so.
When I configure two databases for a namespace I use a single resource for both, I consider this a good practice but, again, this cannot be assumed.
A generalized solution should find the resources used by the installation destination namespace.
This is how you can get the databases used by the namespace "MYAPP":
%SYS>Set sc=##Class(Config.Namespaces).Get("MYAPP",.NsProperties)
%SYS>Write NsProperties("Routines")
MYAPP-R
%SYS>Write NsProperties("Globals")
MYAPP-GNow, for each database you can get the associated resource with:
%SYS>Set dbr=##class(SYS.Database).%OpenId(##class(Config.Databases).GetDirectory(NsProperties("Routines")))
%SYS>Write dbr.ResourceName%DB_MYAPP%SYS>
%SYS>Set dbg=##class(SYS.Database).%OpenId(##class(Config.Databases).GetDirectory(NsProperties("Globals")))
%SYS>Write dbg.ResourceName%DB_MYAPPIn this case for the MYAPP namespace you only need permission to the %DB_MYAPP resource.
If the two databases use different resources, then you need permission to both the associated resources.
Hi @Benjamin De Boe 😊
In general I agree 100%, BUT, if you need to add a favorite now, then the "right thing to do" would be:
Another option is to fix it yourself, the problem is due to the property Data in %SYS.Portal.Users class that is defined as %List but in fact is used by the portal code as a string.
The "misuse" of Data property went unnoticed until version 2025 that implements/introduce %IsValid() method for %List datatype and that's why the %AddFavorite() method does not work anymore. Note that if you call %AddFavorite() passing (correctly) a %List ($list), then the method works, the favorite does not, because the portal code that read the Data property treat it as a String, not a $List!
Fixing this is simple, just edit %SYS.Portal.Users class (after modifying IRISLIB db to R/W and back when you are done...) and change Data datatype from %List to %String.
OR....just set the damn global, enjoy your favorite and go back to business! 😁
Anyone willing to report this to WRC? ...not me 😉
Yes, you need to create your own class that Extends Ens.BusinessService that use Ens.InboundAdapter and and copy or call my code from OnProcessInput() method.
You can ignore pInput & pOutput arguments.
In these cases I use Ens.InboudAdapter with a very high call interval so it runs only once when the BS is enabled (START action in schedule definition)
While the suggested method should still work (haven't tested), I'd advise using the official method $system.SQL.Schema.ExportDDL()
See Class Reference for documentation details and samples.
First you MUST be positively sure on what the character set is actually used in the incoming HL7 message.
What's the character set of the incoming message?
If it's really utf-8 then setting "Default Char Encoding" to utf-8 should work, if it does not, then evidently the incoming message use a different character set.
Just in case, try setting "Default Char Encoding" to "!utf8"
For the incoming HL7 messages, does the field MSH:CharacterSet (MSH:18) contains a value? Is so, what's the value?
You write that the receiver message is encoded using utf-8, have you tried to configure the "Default Char Encoding" setting in the Business Service to "utf-8"?
If the incoming HL7 message is actually/really encoded using utf-8 and MSH:18 contains a value different than "utf-8", you can enforce conversion configuring the "Default Char Encoding" setting in the Business Service to "!utf-8".
I hope the message is not malformed and contains data/fields with different codes, if so...it can be tricky.
Character set conversion should be /configured/applied in the receiving service/adapter.
Unfortunately you did not provide any useful details, so it's impossible to help.