Gertjan Klein · Aug 10, 2017 go to post

Unfortunately, long strings can't be 3GB, but rather almost 3.5MB. For any sane usage of JSON this should be enough, but I've seen REST APIs where e.g. PDF streams are returned in base64-encoded JSON properties, that come uncomfortably close to that size.

It would be really nice if the %DynamicObject class added support for streams to deal with these large payloads!

Gertjan Klein · Aug 17, 2017 go to post

It appears to be enabled when I try here, but I haven't used recordmaps much so I don't have one fully configured. I see another setting for the encoding in the record map properties itself; perhaps this is where things should be configured? (That would also explain why the adapter setting is disabled: you want to configure this only once.)

Gertjan Klein · Nov 22, 2017 go to post

If you add a property with an XMLPROJECTION set to CONTENT, it should be output without the wrapping tags. E.g.:

Property content As %String(XMLPROJECTION = "CONTENT");

Regards,
Gertjan.

Gertjan Klein · Apr 16, 2018 go to post

This datatype is not present in (currently latest) Caché 2017.2; it appears to be IRIS-only. That rather limits its usefullness. Besides, what is the performance of a datatype, and where is that a bottleneck?

Gertjan Klein · Jun 3, 2019 go to post

That link works. But if I search with Google (or DuckDuckGo), the first link that comes up for me appears to point to the proper page, but opening it loads a page about  SOAP Session Management. Many other links from Google end up on that page as well. The key in the docbook URL is ITECHREF_macro, that looks ok, so it seems the ISC docs are broken somehow.

Gertjan Klein · Oct 27, 2019 go to post

This looks like a convenient way to handle things. It would be good, though, if someone could explain what the various ObjectScript commands in irissession.sh are needed for. I also see no credentials being passed?

Gertjan Klein · Oct 28, 2019 go to post

Thanks for that, Evgeny, that does clear up things a bit. I hope Luca responds as well about the Quiesce... and SetMonitorState calls. I get the impression that things relating to creating containers are still changing rapidly. I do find the backslash-escaped multiline commands annoyingly ugly; your post above made that a bit better, compared to what was there before. (I'm hoping some of the ugly boilerplate will be delt with by ISC "inside" the container eventually.) I'm going to play around with your template a bit when I get the time!

Gertjan Klein · Oct 29, 2019 go to post

I have enabled and used that feature in older versions as well; it is really useful. I have a PuTTY setup with a key pair that starts the IRIS terminal in docker on a remote machine, without any password, but still safe. (I wanted to use my regular account name as well, so I had to do some additional setup in the container, but the principle remains the same.)

Anyway, that explains why there's no need for a password anymore, thanks!

By the way, I have not een an announcement that 2019.3 became an official release, did I miss something? (I also see no Studio for 2019.3, and the 2019.4 preview Studio download doesn't work.)

Gertjan Klein · Nov 10, 2019 go to post

This should have been the accepted answer. :) It is more important for code to be easy to read than for it to be easy to write.

Gertjan Klein · Aug 17, 2017 go to post

One of the settings for this service (category Additional settings) is Charset. This allows you to specify the file charset. Did you try setting this to UTF-8?

Gertjan Klein · Jan 4, 2018 go to post

The error message tells you what the problem is. It claims there is no end tag for CardType. If you look closely, you'll see that there is a space between "</" and "CardType>". Remove that, and the message will parse correctly. (Kudos for giving complete information and copy/pasting instead of retyping!)

Gertjan Klein · Apr 24, 2018 go to post

I have not run into this problem. Also, it appears that Caché already adds the Content-Length header:

  Set req = ##class(%Net.HttpRequest).%New()
  Set req.Server = "www.google.com"
  Do req.EntityBody.Write("test")
  Do req.Put("/", 1)

Yields:

PUT / HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; Cache;)
Host: www.google.com
Accept-Encoding: gzip
Content-Length: 4
Content-Type: text/html; charset=UTF-8
test
 

I suspect Content-Length isn't the actual issue. I don't have any ideas about what is, though. (Perhaps you should at least check if your Caché sends it.)

Gertjan Klein · Feb 22, 2019 go to post

If you massage the timestamp format a bit so it is ISO8601/XSD compatible, you can use the XSDToLogical method of %TimeStamp to do the conversion for you:

DEV>Set ts = "2018-02-01 00:00:00+0600"
DEV>Set ts = $Translate(ts, " ", "T")
DEV>Set ts = $Extract(ts, 1, *-2)_":"_$Extract(ts, *-1, *)
DEV>Write ts
2018-02-01T00:00:00+06:00
DEV>Write ##class(%TimeStamp).XSDToLogical(ts)
2018-01-31 18:00:00

Gertjan Klein · Nov 15, 2019 go to post

Studio behaves really annoyingly if it loses its TCP/IP connection, as you described. I don't know how to prevent that. You probably don't have to restart HealthShare, though. When the crash happens, don't close the dialog immediately. Instead, first remove all locks for the process Studio was connected to; it shouldn't be too hard to find. (Something like System Operation -> Locks -> Manage Locks, I don't have access to a HealthShare instance right now.) Then allow Studio to reconnect, which should now work without issues.

Gertjan Klein · Jun 17, 2020 go to post

Can you cut this down to a minimal example (actual code)? What you describe seems to work here. What "doesn't work"?

If I iterate over a (list of) property:

this ultimately compiles to:

As you can see, the .(i) is replaced with .GetAt(i). (Note that I replace the default key "k1" with "i" here to match your example.)

Regards,
Gertjan.

Gertjan Klein · Jun 18, 2020 go to post

Ah, I see -- yes, I can reproduce this, and I would think this is a bug. (It does work as expected in an "if", but not in a "case".) You may want to take this up with WRC, to see what they think.

As a workaround, are you aware that you can use local variables? You could assign the value of source.Items.(i) to a local variable, and use that in the case statement. Something like this:

In a switch statement like yours, this would probably save some typing as well. It does mean the lines in the DTL no longer display properly, though. (Although they might not anyway here.)

Regards,
Gertjan.

Gertjan Klein · Jun 25, 2020 go to post

$System.OBJ doesn't handle CSP pages, but $System.CSP does. You can use LoadPage:

Set sc = $System.CSP.LoadPage("/dev/test.csp", "duck")

(You need to use the URL here, not the class name.) This compiles the CSP page into a class, and then compiles that class.

Gertjan Klein · Sep 28, 2020 go to post

I use %Status exclusively; I really, really don't like try/catch. The most important reason for me is that I want to add information about what went wrong to the status. In e.g. obj.%Save(), the returned status tells me that saving an object went wrong, and hopefully why. I want to add to this which object could not be saved, and possibly some other state that may be relevant for debugging the problem. I find this creates code that is easy to read and debug.

By the way, "If 'sc" is, to me, a lot easier on the eyes than "If $$$ISERR(sc)"...

Gertjan Klein · Sep 29, 2020 go to post

I don't know if try/catch is slow, and I don't care. I don't use it because it is too wordy to handle errors exactly where they occur. It encourages code like in your example, where the code in methods is wrapped entirely in a try/catch block. You say the error object has all the information, but I disagree. It has some low-level information, but often lacks the context I need to determine what the problem is.

My preferred way of handling %Status errors is to add a %Status in front of it with more details of what happened when the error occurred, and return this to the caller. Somewhere up the call chain something will then handle the problem, e.g. add something to the Ensemble event log. This is such a standard way of working for me that I created a macro specifically for prefixing the new status information.

For errors that "raise" I also prefer $ZTrap+$ZError; I don't see the added value of try/catch here either.

Gertjan Klein · Nov 27, 2020 go to post

As you're using Ensemble, you can use class EnsLib.EDI.XML.Document. I don't have version 2015, but the following works on IRIS. I hope this is present in Ensemble 2015 as well. (Note that, annoyingly, method domGetValueAt is marked internal, and therefore doesn't show up in the documentation. I don't know how to achieve the same result without using this method.)

Set XML = "<a><b><c>some content</c></b></a>"Set Path = "/a/b"Set Doc = ##class(EnsLib.EDI.XML.Document).ImportFromString(XML, .sc)If 'sc Quit $System.Status.DisplayError(sc)Set sc = Doc.domGetValueAt(.Value, Path, "fo", 1)If 'sc Quit $System.Status.DisplayError(sc)
Write Value,!

This outputs "<b><c>some content</c></b>" as you want.

Hope this helps,
Gertjan.

Gertjan Klein · Apr 24, 2021 go to post

Thanks, but a terminal command (requiring you to install the preview release) is not what I call documentation. But I did install it. To give an example of what the "Help()" gives me:

Help(method)
     Write out a list of the methods of this object to the console.
addToPath(path)
     <p>
createServer(serverDef)
     Create a new server.  This function requires the "%Admin_Manage" resource.

Interestingly, on the local installed preview, the class documentation does show the %SYSTEM.external class (with ever so slightly more information, btw.). I really hope that this lack of documentation is fixed before release, because as it is now, it is unusable.

Regard,
Gertjan.

Gertjan Klein · May 23, 2021 go to post

Thanks, this is useful information. I have encountered this bug in the past, but did not have the time to figure out exactly what triggered it, and restructured my code.

It would be nice if this bug was fixed, though.

Gertjan Klein · May 24, 2021 go to post

Actually, on closer examination, it appears that in your example the problem is that the Continue is inside the Scope. If you move it outside the Scope, the problem doesn't occur (as you noted). The cause is that the fault handler that was pushed on the stack for the Scope is not removed, because the Scope is not exited "normally". Therefore, each iteration adds a fault handler to the stack that is never removed.

Having one or more scopes in a loop is a perfectly normal thing to do, if you want fine-grained error handling and/or recovery. I have just done some tests, and they work perfectly. If "best practice" dictates that this should not be done, "best practice" is wrong. A Continue inside a Scope, however, is never needed (even if perhaps convenient sometimes).

Unfortunately, I don't remember exactly what I did when I ran into my handler stack issue (it's been a while). Perhaps I also did a Continue inside a Scope. In that case, it was a bug on my part.

Perhaps this can be better documented? The description of the limitation you quoted above is rather vague. Even better would be if the compiler recognized this construct, and removed as many fault handlers from the stack as were pushed on it, when it encounters a Continue inside a Scope in a loop.

Gertjan Klein · May 24, 2021 go to post

Just a quick response to your "topic for a different discussion", as it is relevant in this one. A Scope inside the loop allows one to examine the exact error, and if it is not fatal, decide to handle/ignore it and continue with the next iteration. A Scope outside the loop makes this a lot harder.

What BPL structure shows the problem solution most clearly depends on the problem and sometimes, as you note, on personal preference. It is therefore important to specify exactly which conditions are not supported, rather than "certain conditions". This allows the programmer to choose the easiest and/or clearest solution path, and not have to worry about things breaking if iterations exceed a certain (unspecified) number.

To purposefully withhold that information, as you suggest the documentation does, is saddening.

Gertjan Klein · Feb 16, 2022 go to post

The ToXML function is broken. This is a known bug (I've reported it mid-Januari).

It is possible to fix it, but it requires patching system code, class HS.FHIR.DTL.Util.XML.Adapter to be precise.

In that class, at (on my system, 2021.2) line 359 (in method ToXMLHelper), there is this statement:

set isprimitive = ($extract(propType)="%")

This basically assumes all properties are objects, except those that start with a %-sign. This is obviously wrong. The code will work if you replace that statement with this:

set isprimitive = ($extract(propType)="%") || (propType="") || ($$$classIsDataType(propType))

Here we additionally check for properties without a type (DomainResource has one: property id), and typed properties that are datatypes. With this change, the ToXML() method works for me.

HTH,
Gertjan.

Gertjan Klein · Jun 30, 2022 go to post

Method %JSONImport returns a %Status, which tells you what the problem is (using $System.Status.DisplayError()):
ERROR #9406: Unexpected format for value of field, appointmentid, using AthenaAppointment mapping
That value is a number in JSON, but you defined it as a %String in the class. The JSON import code disapproves. There are more fields like that, and additionally field reasonid is not defined in the mapping. If you fix these problems, the data will import.

(I'd also remove the %DynamicAbstractObject superclass, it is unneeded and gives errors on object destruction.)

Gertjan Klein · Jul 4, 2022 go to post

I've seen this happen on a CE docker instance; in this case the cause was that licenses ran out. I did not investigate further, but it appeared each start of a web terminal caused a new license to be consumed. Perhaps this could be your problem?

Gertjan Klein · Jul 5, 2022 go to post

You can check whether you still have licenses available in the management portal: System Operation -> License Usage. If Current License Units Used equals License Units Authorized, web terminal can't allocate a new license, which it does seem to need. If not, yours is a different problem.