Hi Luis,

This is awesome, and just what I was currently engaged with Support about! Talk about good timing...

A couple of questions:

In your "broadcast" routine, you have the read of the Websocket in IRIS like:

Set data= ws.Read(, .status, )

The docs say that the format of that call is:

method Read(ByRef len As %Integer = 32656, ByRef sc As %Status, timeout As %Integer = 86400) as %String
 

Won't that mean that if your WebSocket is open (but the user is out to lunch), you will hang for 86400 seconds before returning?

Second, do you know what the licensing impact on IRIS is for this? Say there was 100 notification targets,. Would the system take out 100 License Units in this loop?

Thanks!

Richard

Thanks Matthew!

Can you help with how you would enable CORS handling using Apps.REST?

I've done a few things, but with not much luck:

In AppS.REST>Handler, I added Cors = "true" to each of the <Route> elements in UrlMap, and then added the over-ride ClassMethod OnHandleCorsRequest as well, with the standard default processing:

ClassMethod OnHandleCorsRequest(pUrl As %String) As %Status
{
Do %response.SetHeader("Access-Control-Allow-Origin","*")
    Do %response.SetHeader("Access-Control-Allow-Credentials","true")
    Do %response.SetHeader("Access-Control-Allow-Methods","GET, PUT, POST, DELETE, PATCH, OPTIONS")
    Do %response.SetHeader("Access-Control-Allow-Headers","Access-Control-*, Content-Type, Authorization, Accept, Accept-Language, X-Requested-With, Origin")
    
    Quit $$$OK
}
 

Thanks Matthew!

Can you help with how you would enable CORS handling using Apps.REST?

I've done a few things, but with not much luck:

In AppS.REST>Handler, I added Cors = "true" to each of the <Route> elements in UrlMap, and then added the over-ride ClassMethod OnHandleCorsRequest as well, with the standard default processing:

ClassMethod OnHandleCorsRequest(pUrl As %String) As %Status
{
Do %response.SetHeader("Access-Control-Allow-Origin","*")
    Do %response.SetHeader("Access-Control-Allow-Credentials","true")
    Do %response.SetHeader("Access-Control-Allow-Methods","GET, PUT, POST, DELETE, PATCH, OPTIONS")
    Do %response.SetHeader("Access-Control-Allow-Headers","Access-Control-*, Content-Type, Authorization, Accept, Accept-Language, X-Requested-With, Origin")
    
    Quit $$$OK
}
 

@Timothy Leavitt  - stuck again.

I'm in ClassMethod UserInfo, and found out some interesting things.

First off, I was wrong about the REST service using the session cookie from the Zen application when it is called from the Zen application. Displaying the %session.SessionId parameters for each call shows that they are all different, and not the same as the SessionId of the Zen application calling the REST service. So the idea that it holds a license for 10 seconds can't be correct, as it seems almost immediate. I run 20 REST calls to different endpoints in a loop, and I saw a single License increase.

You said I should be able to expose the session cookie of the Zen application, but I don't see a way to do that either.

I can't even find a way to see the header data in the UserInfo ClassMethod of the current REST call.

Sorry to be a pest...but since you''re giving answers, I'll keep asking questions!

Have a nice evening...

@Timothy Leavitt  - thanks so much for the response. The Action worked perfectly with your corrections!

I will take your advice and work with the %session/headers in the context object, since that makes the most sense.

What are the plans (if any) to enable features in a resultset such as pagination, filters, and sorting?

Users are horrible, aren't they? No matter what good work you do, they always want more! I appreciate what you have done here, and it will save my company probably hundreds of hours of work, plus it is very elegant...

@Timothy Leavitt , I will be looking for it.

I'm trying to do something with a custom Header that I want to provide for  the REST calls. Do I have access to the REST Header somewhere in the service that I can pull the values, like a %request?

And in something of an edge case, we're calling these REST services from an existing ZEN application (for now as we start a slow pull away from Zen), so the ZEN app gets a %Session created for it, and then calls the REST service. It seems that Intersystems is managing the License by recognizing that the browser has a session cookie, and it doesn't burn a License for the REST call - that's very nice (but I do have a request in to the WRC about whether that is expected behavior or not so I don't get surprised if it gets "fixed"!). Does that mean your REST service can see that %Session, as that would be very helpful, since we store User/Multi-tenant ID, and other important things in there (the %Session, not the cookie).

I posted an issue with my source to Github.

Surfaced another issue this week-end. (I remember when I used to take week-ends off, but no whining!)

So I have a multiple linked series of classes in Parent/Child relationships:

DocHead->DocItems->DocItemsBOM->DocItemsBOMSerial

So if I wanted to express all of this in a JSON object, I would need to make the "Default" mapping the one that exposes all the Child Properties, because it looks like I can't control the Mapping of the Child classes from the Parent class.

This doesn't bother me, as I had already written a shell that does this, and your Proxy/Adaptor makes it work even better, but just wanted to check that the Parent can't tell the Child what Proxy the child should use to display its JSON. It's even more complicated than that, as sometimes I want to show DocHead->DocItems (and stop), while, in other Use Cases, I have to show DocHead, DocItems, and DocItemsBOM (and stop), while in other Use Cases, I need the entire stack.

@Timothy Leavitt, I've run into another issue.

The proxy is setup and working great for general GET access. But since my system is a multi-tenant, wide open queries are not a thing I can use, so I decided to try to use a defined class Query in the data class Lookups.Terms:

Query ContactsForClientID(cClientOID As %String) As %SQLQuery
{
SELECT 
FROM Lookups.Terms
WHERE ClientID = :cClientOID
ORDER BY TermsCode
}

Then I setup the Action Mapping in my proxy class RESTProxies.Lookups.Terms.Base:

XData ActionMap [ XMLNamespace = "http://www.intersystems.com/apps/rest/action]
{
<actions xmlns="http://www.intersystems.com/apps/rest/action">
<action name="byClientID" target="class" method="GET" 
modelClass="Lookups.Terms" query="Lookups.Terms:ContactsForClientID">
<argument name="clientid" target="cClientOID" source="url"/>
</action>
</actions>
}

And I invoked this using this URL in a GET call using Postman (last part only):

terms_base/$byClientID?clientid=290

And the result:

406 - Client browser does not accept the MIME type of the requested page.

In the request, I verified that both Content-Type and Accept are set to application/json (snip from the Postman):

So what have I missed?

That did the trick - thank you so much!

Best practice check: When I have a data class (like Data.DocHead) that will need multiple Mappings (Base, Expanded, Reports), then the recommended way is to use the proxy class and have a different proxy class for Data.DocHead for each mapping?

For example, RESTProxies.Data.DocHead.Base.cls would be the proxy for the Base mapping in Data.DocHead, while RESTProxies.Data.DocHead.Expanded.cls would be the proxy for the Expanded mapping in Data.DocHead, etc. (the only difference might be the values for the JSONMAPPING and RESOURCENAME prameters)? I'm fine with that, just checking that you don't have some other clever way of doing that...

This is really cool, and we will be using this in a big way.

But I have encountered an issue I can't fix.

I took one of my data classes (Data.DocHead) and had it inherit from  AppS.REST.Model.Adaptor and %JSON.Adaptor, set the RESOURCENAME and other things and tested using Postman and it worked perfectly! Excellent!

Due to the need to have multiple endpoints for that class for different use cases, I figured I would set it up using the  AppS.REST.Model.Proxy, so I created a new class for the Proxy, removed the inheritance in the data class (left %JSON.Adaptor), deleted the RESOURCENAME and other stuff in the data class.

I used the same RESOURCENAME in the proxy that I had used in data class originally.

I compiled the proxy class, and get the message:

ERROR #5001: Resource 'dochead', media type 'application/json' is already in use by class Data.DocHead
  > ERROR #5090: An error has occurred while creating projection RestProxies.Data.DocHead:ResourceMap.

I've recompiled the entire application with no luck. So there must be a resource defined somewhere that is holding dochead like it was still attached to Data.Dochead via a RESOURCENAME, but that parameter is not in that class anymore.

How do I clear that resource so I can use it in the proxy?