go to post Sean Connelly · Oct 31, 2018 Hi Thiago,TryD:\>csession CACHE20172 -U %SYS "##class(test.MyClass).MyMethod(""""""test"""""")"
go to post Sean Connelly · Oct 30, 2018 Hi Keshav,%ID is a psuedo field that will always reference the RowID, by default this is named as ID, but could also be ID1, ID2 .. IDnMore about it here...https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_tables#GSQL_tables_idfieldNot sure about your error, would need to see your full code.If you are not naming properties as ID, then just use ID.Sean.
go to post Sean Connelly · Oct 30, 2018 A couple of thoughts, as you have a backslash in the path, the new file path is going to be a concat of ..Adapter.%serverPath and NewFilePath, I would log this and not just the fullPath so you can be sure what directory you are trying to write to... $$$TRACE("Path for archive: "_..Adapter.%serverPath_NewFilePath) Also, might just be worth checking that you are connected when you do the rename $$$TRACE("Connected: ",..Adapter.Connected)
go to post Sean Connelly · Oct 30, 2018 I just re-read and see you have tried from a different client, maybe Ensemble is losing the connection, I will have a look around the code.
go to post Sean Connelly · Oct 30, 2018 Hi Francisco,At a guess, I would say your FTP user account doesn't have permission to write into the archive path.
go to post Sean Connelly · Oct 29, 2018 Hi Stephen,Interesting question.The routing engine gets passed a context object that contains a Document object. This is the request object, so in your example you are directly accessing your Ens.AlertRequest with Document.AlertText.I did a full dump of the context object so you can see what properties are available... { "ActionTargets":"Foo.Productions.TEST2RoutingRule", "Adapter":"", "AlertGroups":"", "AlertOnBadMessage":null, "AlertOnError":false, "AlertRetryGracePeriod":0, "BadMessageHandler":"", "BusinessPartner":"", "BusinessRuleName":"Foo.Productions.TEST2RoutingRule", "Document":{ "AlertDestination":"The alert destination", "AlertText":"The alert text", "AlertTime":"2018-10-29 15:29:18.517", "SessionId":null, "SourceConfigName":"souce" }, "FailureTimeout":15, "ForceSyncSend":null, "InactivityTimeout":0, "MsgClass":"Ens.AlertRequest", "QueueCountAlert":0, "QueueWaitAlert":0, "ReplyCodeActions":"", "ResponseFrom":"", "ResponseTargetConfigNames":"", "ResponseTimeout":-1, "Retry":false, "RetryInterval":5, "Source":"EnsLib.Testing.Process", "SuspendMessage":false, "ThrottleDelay":0, "Validation":"", "aRespFrom":{ } } There doesn't look to be any access to the Header object on the context object. So I did a dump of the stack to see if its visible outside of the context object, but it looks like the routing rules are scoped off to just the arguments it takes... { "Objects":{ "pContext":{ "Properties":{ "ActionTargets":"Foo.Productions.TEST2RoutingRule", "Adapter":"", "AlertGroups":"", "AlertOnBadMessage":null, "AlertOnError":false, "AlertRetryGracePeriod":0, "BadMessageHandler":"", "BusinessPartner":"", "BusinessRuleName":"Foo.Productions.TEST2RoutingRule", "Document":{ "AlertDestination":"3", "AlertText":"2", "AlertTime":"2018-10-29 15:36:24.273", "SessionId":null, "SourceConfigName":"1" }, "FailureTimeout":15, "ForceSyncSend":null, "InactivityTimeout":0, "MsgClass":"Ens.AlertRequest", "QueueCountAlert":0, "QueueWaitAlert":0, "ReplyCodeActions":"", "ResponseFrom":"", "ResponseTargetConfigNames":"", "ResponseTimeout":-1, "Retry":false, "RetryInterval":5, "Source":"EnsLib.Testing.Process", "SuspendMessage":false, "ThrottleDelay":0, "Validation":"", "aRespFrom":{ } }, "Reference":"3@EnsLib.MsgRouter.RoutingEngine" } }, "Primatives":{ "pEffectiveBegin":"", "pEffectiveEnd":"", "pPassed":"1", "pReason":"", "pReturnValue":"", "pRuleSet":"", "tSC":"1" }, "Stack":{ "1":"zStart+62^Ens.Job.1 +2 : Set tInstance.%QuitTask=0, tSC=tInstance.OnTask() Quit:('tSC)||tInstance.%QuitTask", "2":"zOnTask+42^Ens.Host.1 +1 : Set tSC=..MessageHeaderHandler(tRequestHeader,.tResponseHeader)", "3":"zMessageHeaderHandler+42^Ens.Actor.1 +1 : Set tSC=tBP.MessageHeaderHandler(pRequestHeader,.pResponseHeader,.tHandledError) ; Sets SessionId, we clear it", "4":"zMessageHeaderHandler+18^Ens.BusinessProcess.1 +1 : Set tSC=..OnRequest(..%request,.tResponse)", "5":"zOnRequest+21^EnsLib.MsgRouter.RoutingEngine.1 +1 : Quit ..doOneAction(request,\"rule:\"_tRuleName,,,.response) }", "6":"zdoOneAction+8^EnsLib.MsgRouter.RoutingEngine.1 +1 : Set tSC=##class(Ens.Rule.RuleDefinition).EvaluateRulesEx(tOneTarg, ..%SessionId, $this, $classname(), .tActionList) Quit:('tSC)", "7":"zEvaluateRulesEx+3^Ens.Rule.RuleDefinition.1 +1 : Quit ##class(Ens.Rule.Definition).EvaluateRules(pRuleName,pSessionId,pContext,pActivityName,.pReturnValue,.pReason)", "8":"zEvaluateRules+12^Ens.Rule.Definition.1 +1 : set tSC=$classmethod(tClassName,\"evaluateRuleDefinition\",pContext,.tRuleSet,.tEffectiveBegin,.tEffectiveEnd,.pReturnValue,.pReason)", } } So I think the short answer is going to be not directly. I can access the request ID in the code behind, using the %Id() method, but the rule validator doesn't seem to like referencing it. I had a quick hack and managed to find one way by using a custom function, its a hack but something like this, but maybe using dynamic SQL to access the value... Class Foo.FSET Extends Ens.Rule.FunctionSet { ClassMethod HeaderPropertyEquals(obj, name, value) { set bid=obj.%Id() &sql(select id into :id from Ens.MessageHeader where MessageBodyId=:bid) set mh=##class(Ens.MessageHeader).%OpenId(id) quit $property(mh,name)=value } } You can then call this function in the rule set as a condition... HeaderPropertyEquals(Document,"SourceConfigName","Foo Service") Can't say I have ever wanted to access the Header object, but interesting to hack around the code behind to see whats going on. Sean
go to post Sean Connelly · Oct 29, 2018 Hi Sabit,This code looks familiar, a 5 minute hack from another answer I made The problem is probably with %File, try replacing the file output with the following code... set file=##class(%FileBinaryStream).%New() set file.Filename=filename quit file.OutputToDevice() If you are still getting a file not found then the file name is probably wrong / corrupt, try logging the file name and opening the file from the command line to be sure. Sean.
go to post Sean Connelly · Oct 29, 2018 Hi Arun,Looks like the test form doesn't support the payload property type.You will have more luck creating the request object from the command line and then calling the test service, something along these lines... set sc=##class(EnsLib.Testing.Service).SendTestRequest("Put Target Name Here",request,.response,.sessionId,1) You will need to create an instance of a stream object and assign it to the Stream property of the request object. If you don't have a WSDL then you could try contacting the TRUD for ITK documentation... https://isd.digital.nhs.uk/trud3/user/guest/group/0/pack/30 There is one tip that I use when I don't have a WSDL. I take an example XML message and create an XSD for it, there are online generators that can help you do this, such as this one here... https://www.freeformatter.com/xsd-generator.html You can then paste the XSD into the XML Schema Wizard found in Studio > Tools > Add-Ins > Add-Ins, this will generate a class that you can use as your request object. Sean.
go to post Sean Connelly · Oct 18, 2018 I was just thinking, if you are on a schema before 2.4 then you might not have sub fields to do this.In which case here is a quick hack, set the value to...$Piece(target.{PID:PhoneNumberHome(1)},"^",1)Long term its better to customise your schema's.
go to post Sean Connelly · Oct 18, 2018 These are paths from a 2.4 specification. You will need to figure out the paths that are specific to your schema. Try these steps...1. Create a set on PID:13.12. Cut the path from the property box3. Delete that set4. Create a set on PID:13 (not PID:13.1)5. Paste in the path you cut into the value box
go to post Sean Connelly · Oct 18, 2018 If you can't / don't want to fix the source schema then you can always copy values from the target, assuming it does have a schema.So for instance your would do a set on...target.{PID:PhoneNumberHome(1)}And give it the value...target.{PID:PhoneNumberHome(1).phonenumber1}This will replace the entire target field with just the target phone number.
go to post Sean Connelly · Oct 18, 2018 Hi Tiago,The ? pattern match is native to COS and has been in the product since day 1. You will also find it in GT.m and a few other fringe M systems, but probably nowhere else.The $match feature was introduced back in 2012. It uses an external Regex library called ICU.Both are highly optimised for general use cases (millions of operations per second).The ? pattern matching syntax is less expressive than Regex, but this also makes it easy to learn and remember.Regex would be the better choice for more complex pattern matching requirements.Sean.
go to post Sean Connelly · Oct 17, 2018 typing too fast, should have pointed out the -1 was for reference to trim at the other endyou can get a full list of these type of functions here...https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_FUNCTIONSand a bit more information specifically on working with strings here...https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCOS_strings
go to post Sean Connelly · Oct 17, 2018 Thanks Robert, I didn't realise indirection itself would work on an object property, far too obvious now, lol.
go to post Sean Connelly · Oct 17, 2018 OK, so I guess I could use xecute for this... xecute ("(obj,key) set key=$order(obj."_prop_"(key))",obj,.key)
go to post Sean Connelly · Oct 15, 2018 Hi Oliver,The Transform is a generated method, it doesn't make sense to override it and call its base implementation, hence the ##super() error.If you want the Transform status code then just call the generated Transform on your transform class from the command line, e.g. write ##class(My.Transform).Transform(in,.out) But you are not going to get anything meaningful from this unless you actual capture and return the error, so you might want some code inside your transform like this... Set tSCTrans=target.%Save() if ('tSCTrans) goto Exit But this doesn't feel right. I would have a custom process and do it this way... set sc=##class(My.Transform).Transform(in,.out) if $$$ISERR(sc) quit sc set sc=out.%Save() quit sc You will quit the status code up to the ensemble director that will then handle the error logging for you. And I bet you a pint that one of your X12 fields is longer than 50 characters and you are getting a MAXLEN error returned from the save method. Sean.
go to post Sean Connelly · Oct 10, 2018 Hi Richard,You can create and delete accounts via the %SYS namespace using the following class methods...https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=Security.UsersIt sounds like you might want to take a look at delegated authentication as well...https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_delegatedIf this is just for a CSP application then you can detect the user logging out or a session timeout using the %CSP.SessionEvents class.Sean.
go to post Sean Connelly · Oct 10, 2018 Hi Mauri,The short answer is going to be not via the reply code actions.You can customise an operation to send a message back around (..SendRequestAsync for instance) which would put it to the back of the queue.You don't mention the type of message or error that would require this type of behaviour. It does seem a little odd since a message that fails either fails because of a network / end point down problem, or that the message data has failed validation. In the first instance you would most likely want to just keep retrying the same message with a failure timeout of -1, and in the second instance, a message that fails validation would surely keep failing validation until you manually fixed and resent the problem by hand.Sean.
go to post Sean Connelly · Oct 10, 2018 Hi Sansa,If you are getting back an empty string (I suspect not null) then you should be able to use the GetError method to investigate the problem. This is from the class method comments... If the function fails, it returns "". The actual session error status can be returned by a call to the GetError method. Examples: s Status=##Class(%SYS.LDAP).SearchExts(LD,BaseDN,$$$LDAPSCOPESUBTREE,Filter,Attributes,0,"","",2,ServerTimeout,.SearchResult) i Status'=$$$LDAPSUCCESS q Status s Entry=##Class(%SYS.LDAP).FirstEntry(LD,SearchResult) s Status=##Class(%SYS.LDAP).GetError(LD) i Status'=$$$LDAPSUCCESS q Status s ValueList=##Class(%SYS.LDAP).GetValues(LD,Entry,"sAMAccountname") i ValueList="" q ##Class(%SYS.LDAP).GetError(LD) w !,"sAMAccountname="_$li(ValueList,1) It's certainly worth looking at the class documentation for this... https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls From experience LDAP can be tricky to get working, and even harder to help diagnose without having all the information at hand. It's analogous to posting the question, my car won't start, whats the problem. It can be 101 things, so you will need to narrow down the error and provide as much information as you can if you are still stuck. Sean.