This means your query is taking a longer time to return the result back to your CSP application than what it expects.

I'd recommend you to verify why it's taking so long and optimize your query, but you can also ask the CSP gateway to wait for a longer period before it times out.

  // On OnPreHTTP callback method.
  set %response.Timeout = 900 // this will make your application wait for the response for 900 seconds.

Note that %response.Timeout will change the timeout for the current request only.

You mean like this?

ClassMethod CallHook(hook As %String = "", args...) As %Status [ Private ]
{
  if hook = "" return $$$OK

  set classname = $piece(hook, ":", 1)
  set method = $piece(hook, ":", 2)
  try {
    return $classmethod(classname, method, args...)
  } catch ex {
    return ex.AsStatus()
  }
  return $$$OK
}

It certainly does for me.

https://github.com/rfns/frontier/blob/fe0868c8e0821ffdfd15407994288b2832...

EDIT: Now that I have seen this qualifier, I'll be doing some tests to see the result.
EDIT #2: Looks like it works as I intended, so please ignore  the text below. 

Yes, this is really painful if you think about using IRIS to generate artifacts for relatively new Caché versions. They can't read some changes that IRIS made, like:

* IRIS also moves away from %Library.CacheStorage which is used by Caché as it instead now uses %Storage.SQL. It also unifies the storage strategy for both: %Persistent classes and custom storage, which Cache used two distinct storage classes, one being %CacheStorage.
* Some XML elements have been modified or wiped out.
* The XML header now prints generator=IRIS instead of generator=Cache.
* Methods that have [ Language = cache ]. are now converted to [ Language = objectscript ].

This is what lead us to create a custom docker image that hosts Caché instead of IRIS for using it to generate continuous delivery.

Yes, we are on the road for making it a SPA (single page application) using React.

In the short term, we probably won't make it a PWA though since we also have developers with enough knowledge to create mobile applications using React-Native (me included).

Since you mentioned libraries and framework, I'll go a bit further and provide you a history background about why we are using React after dropping Angular:

Several years ago we initially used Angular 1.5 to create partial SPA applications embedded to our legacy CSP application, however as the demands increased we noticed that Angular introduced a spike in the learning curve messing up with our development speed as several domain specific languages had to be researched to fill these demands, including knowledge for advanced usage/creation of directives, really deep understanding of scope and its cycles. A complete mess.

We considered using Angular 2 at that time, but we dropped the idea as we noticed Angular 2 getting close to the component-based approach just like React and since our experiences with Angular were not the best along with the effort put from learning the 1.5 version becoming useless, we started to move to React.

Sadly VueJS was a late player at that time although I do recognize its efforts to use the best both from each worlds.

%CSP.StreamServer is just a helper to cut short some manual labor.

What you need to do is write the file stream back to the device and change three Content-* headers as follows.

Your CSP page has an OnPreHTTP just like every %CSP.Page extended class. You can use this method to modify the Content-Type, Content-Disposition and Content-Length headers. In your case you'll need to use the <script> tag syntax for creating the method.

The example below assumes you're using a class instead.

NOTE: If you really customize your %CSP.Page or %CSP.REST. You don't even need to use the OnPreHTTP method. But I'll be using it here for educational purposes.

ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ]
{
   set file = %request.GetMimeData("FileStream")
   if '$isobject(file) return 0

   do ##class(%CSP.StreamServer).FileClassify("XLS", .contentType, .isBinary, .charset)
   do %response.SetHeader("Content-Type", contentType)
   do %response.SetHeader("Content-Disposition", "attachment; filename=""_file.FileName_""")

  return 1
}

ClassMethod OnPage() As %Status [ ServerOnly = 1 ]
{
  set iStream = %request.GetMimeData("FileStream")
  $$$QuitOnError(##class(something).method(iStream, .oStream))

  do %response.SetHeader("Content-Length", oStream.Size)

  do oStream.Rewind()
  do oStream.OutputToDevice()

  return $$$OK
}

I try to use Studio whenever possible because it feels "faster" for me. This is mostly because it also feels more "native" as well.
I usually fallback to VSCode when I can't but I still find the UX lacking (obviously, it's not a dedicated tool after all).

However this is not the only obstacle: the Atelier API as oposed to Studio feels really slow when I use my instance locally.
Think about it: I have the files locally, so I still have to execute HTTPs request to my own machine even though I could simply use the file system to import these files, having a network layer in this case only introduces a bottleneck.

Also, the Atelier API doesn't seems to be optimized to handle large projects (more than 5000 items) which in turn reflects to the VSCode extension.

Here's an use case from my company:

  • We use distributed development and versionment.
  • 3 of our users are Mac based due to working with tools like React-Native, so they have to use VSCode.
  • Since we're adapting from a legacy development style, for now our project has about 3200 items.
  • Our project contains lots of static files from CSP to JS even HTML files. And we are moving away from CSP because we plan on making our product a SPA.
  • We do NOT use IRIS for development and CD due to critical compability issues with Caché versions (try importing a XML generated from IRIS into Caché to see what happens).
  • We use VSCode to edit these JS and HTML files.
  • Releases are XML based and generated using a docker version of Caché.

To be honest, the only thing we use from this poll is Studio, otherwise we had to create our own tool to fit our needs.

Yes, what I meant to say is that the original file is correct. It's not us who did the double transcoding. The resulting output that I posted:

"text": "Condição de pagamento sujeito a análise de crédito: "

Is straight from the call from Export and/or ExportToStream. Which is why I said that these methods seems to impose a transcoding step.

This is weird, I shouldn't have to convert a file to RAW in order to export to UTF-8. But instead provide the same charset for both input/output so that the engine actually knows which encoding to use (but not transcode).

Unless there's is a way to effectively disable that hidden transcoding step that these method do, this make these methods really misleading.

Hello @Alexander Koblov.

I also did the test using Export instead of ExportToStream and got the same result.

Now first thing, you must make sure that the file you used is indeed written using UTF-8.

You can check it by using the following command:

file -bi ..\..\csp\user\test.json

It should display:

charset=utf-8

Now regarding more tests I did, it seems like there's an imposed transcoding step when exporting the file. I ran several simulations with many type of combinations:

  • When the original is file written using UTF-8 and I exported using UTF8 it broke the encoding.
  • When the original is file written using UTF-8 and I exported using RAW (which is ISO-8859-1 in my case), it DID NOT broke the encoding.
  • When the original is file written using ISO-8859-1 and I exported using RAW, it DID NOT broke the encoding.
  • When the original is file written using ISO-8859-1 and I exported using UTF8 it DID NOT broke the encoding.

This is very strange.

I cannot see any need to combine both in the same instruction, both return null if the item does not exist.

I wanted to avoid writing more than one line or having to get the value twice, e.g.

set value = array.GetAt("key")
if value = "" set value = "default"

I actually did it that way. But I could do it this way too:

set value = $select(array.GetAt("key") '= "" array.GetAt("key"), 1: "default")

But now I would be calling it twice which seemed like code smell for me.

Ahh, yes, it could be used that way too.

Either way, basically only the schema is supporting the DispatchClass, the engine itself doesn't, making it looks like a feature that's in development. So I'm sure that will get this feature on the near future.

Well... here's how I did it:

https://github.com/rfns/iris-ci/blob/master/ci/App/Installer.cls