What is the best practice to read full POST request body when using REST in Caché? - intersystems-cache

Having the following UrlMap:
<Routes>
<Route Url="/SaveSomething" Method="POST" Call="Save"/>
</Routes>
What is the best (easiest?) way to read full request body, without string length limitations ($$$MaxStringLength) or anything that may cut off some data?
In my case the purpose of this is to place the contents into a global.

Something like that would work:
// %request.Content is a %CSP.BinaryStream
set ^global = ""
while '%request.Content.AtEnd {
set ^global = ^global _ %request.Content.Read($$$MaxStringLength)
}
About %request

Related

How to make a REST delete method with cfhttp

I have never done it before and now when the need arise, things are not working.
I have to send an ID to delete a DB record with RESTful service. Here is the code I am trying:
<cfhttp url="http://127.0.0.1:8500/rest/test/something" method="DELETE" port="8500" result="qryRes1">
<cfhttpparam type="body" value="36"/>
</cfhttp>
and in the REST function
remote any function someName() httpmethod="DELETE"{
var testID = ToString(getHTTPRequestData().content);
//make db call to delete
return testid;
}
The result comes as blank [empty string]. I am not able to retrieve the sent value in function. What I am missing?
Edit: one slightly different but related to CF rest, is it necessary to convert query to an array before sending it back to client? Directly serializing won't solve the purpose same way?
you may want to take a look at deleteUser() in http://www.anujgakhar.com/2012/02/20/using-rest-services-in-coldfusion-10/ as an example of how to support DELETE in REST API style.
remote any function deleteUser(numeric userid restargsource="Path") httpmethod="DELETE" restpath="{userid}"
{
var response = "";
var qry = new Query();
var userQry = "";
qry.setSQl("delete from tbluser where id = :userid");
qry.addParam(name="userid", value="#arguments.userid#", cfsqltype="cf_sql_numeric");
userQry = qry.execute().getPrefix();
if(userQry.recordcount)
{
response = "User Deleted";
} else {
throw(type="Restsample.UserNotFoundError", errorCode='404', detail='User not found');
}
return response;
}
As for the 2nd part of your question, it'd be best to first turn a query into a array of structs first unless you're using CF11 which does it for you. See: http://www.raymondcamden.com/index.cfm/2014/5/8/ColdFusion-11s-new-Struct-format-for-JSON-and-how-to-use-it-in-ColdFusion-10
The default JSON structure for query in CF 8 to 10 were designed for <cfgrid> in ColdFusion on top of Adobe's discontinued Spry framework.

How to view TCL REST Response headers

My code sample was
package require rest
set yweather(forecast) {
url http://weather.yahooapis.com/forecastrss
req_args { p: }
opt_args { u: }
}
rest::create_interface yweather
Output
% set res [yweather::forecast -p 94089]
channel {title {content {Yahoo! Weather - Sunnyvale, CA}} .........
But i am trying to view Response header, like Status codes, set-cookie information. I don't know how to view, some one please help to resolve this issue.
Thanks
Typically with handling REST, I'd just use the standard http package directly (or wrapped into a little class). That would let you use http::meta to get at the gory response details, and also let you control much more precisely what message gets sent in the first place (usually rather important!)
However, that's me (as I'm pretty au fait with REST and the http package). Let's dig into the rest package more carefully and get it to do what we want.
By close reading of the documentation, I see that the interface descriptor dictionary allows the keys pre_transform and post_transform, and that the http token is available in the calling context. Let's try with the post_transform…
package require rest
set yweather(forecast) {
url http://weather.yahooapis.com/forecastrss
req_args { p: }
opt_args { u: }
post_transform extract_metadata
}
rest::create_interface yweather
proc extract_metadata {response} {
upvar 1 token token
lappend response [http::meta $token]
return $response
}
Now, if you do:
set res [yweather::forecast -p 94089]
You should get the extra information you want (and far more probably!) in the meta field at the end.

Getting a 204 No Content response when I use restSetResponse in Coldfusion 10 REST

I am trying to use restSetResponse to set my own status and content as described in the docs here:
http://help.adobe.com/en_US/ColdFusion/10.0/Developing/WSe61e35da8d318518-5719eac51353e6bb244-7fec.html
Here is my REST method:
remote any function test() httpmethod="get" {
var response = {
status: 400,
content: 'something is wrong'
};
restSetResponse(response);
}
Any help would be greatly appreciated. Thanks!
In the link you provided, note the first numbered bullet under the "Send custom success responses using restSetResponse" heading. If you define the CFC as REST service, you need to ensure the returnType is set to "void". In your example, you have your returnType set to "any".

Set Mirth destination to send transform data back as a custom ACK

I have a Mirth channel that set up as a web service listener, it receives an ID, build an HL7 query message and send this query and eventually get back an HL7 response.
Channel Name: QueryChanel
Source Connector Type: Web Service Listener
Destination Connector Name: QueryToVista
Destination connector Type:LLP Sender.
This is the typical HL7 response I receive back from my query is as follow:
MSH|~|\&|VAFC RECV|FACILITY|VAFC TRIGGER||20121011141136-0800||ADR~A19|58269|D|2.4|||NE|NE|USA
MSA|AA|1234|
QRD|20121011051137|R|I|500000001|||1^ICN|***500000001***|ICN|NI|
EVN|A1|20121004064809-0800||A1|0^^^^^^^^USVHA\\0363^L^^^NI^TEST FACILITY ID\050\L|20121004064809-0800|050
PID|1|500000001V075322|500000001V075322^^^USVHA\\0363^NI^VA FACILITY ID\050\L~123123123^^^USSSA\\0363^SS^TEST FACILITY ID\050\L~9^^^USVHA\\0363^PI^VA FACILITY ID\050\L||JOHN^DOE^^^^^L|""|19800502|M||""|""^""^""^""^""^^P^""^""~^^""^""^^^N|""|""|""||S|""||123123123|||""|""||||||""||
PD1|||SOFTWARE SERVICE^D^050
ZPD|1||||||||||||||||""
I can get all the above to return if I set my Source's Response From parameter to QueryToVista
However, I want to return only the value 500000001 from the above message. I've tried to play around with the transformer in the QueryChanel destination without success.
Update:
I tried to add a javascriptwriter connector after the QueryToVista connector in the same channel as follow:
var destination = responseMap.get('QueryToVista');
var responseMessage = destination.getMessage();
//Fails with following error: TypeError: Cannot read property "QRD.4" from undefined
var customack = ResponseFactory.getSuccessResponse(responseMessage['QRD']['QRD.4'] ['QRD.4.1'].toString())**
//work but send the whole HL7 message
var customack = ResponseFactory.getSuccessResponse(responseMessage.toString())**
responseMap.put('Barcode', customack);
I can't seem to use the normal transformation to retrieve the element at all.
Thank you.
You're on the right track, but your update illustrates a couple of issues. However, your basic approach of using two destinations is valid, so long as "Synchronize channel" is checked on the Summary tab.
Issue 1
In your example, the HL7 response you are wanting to parse is in pipe delimited HL7 form. In order to access the elements using E4X notation (eg. responseMessage['QRD']['QRD.4']['QRD.4.1']) you must first convert it into an E4X XML object. This can be done in two steps.
Convert the pipe delimited HL7 string into an XML string.
Convert the XML string into an E4X XML object
In a Javascript transformer of the JavaScript Writer (not the Javascript Writer script itself)
var response = responseMap.get("QueryToVista");
var responseStatus = response.getStatus();
// Get's the pipe delimited HL7 string
var responseMessageString = response.getMessage();
if (responseStatus == "SUCCESS")
{
// converts the pipe delimited HL7 string into an XML string
// note: the SerializeFactory object is available for use in transformer
// scripts, but not in the Javascript destination script itself
var responseMessageXMLString = SerializerFactory.getHL7Serializer(false,false,true).toXML(responseMessageString);
// convert the XML string into an E4X XML object
var responseMessageXMLE4X = new XML(responseMessageXMLString);
// grab the value you want
var ack_msg = responseMessageXMLE4X['QRD']['QRD.4']['QRD.4.1'].toString();
channelMap.put('ack_msg', ack_msg)
}
else
{
// responseStatus probably == "FAILURE" but I'm not sure of the full range of possibilities
// take whatever failure action you feel is appropriate
}
Edit**
I don't believe there is an Issue 2. After reviewing your own approach, I played a bit further, and believe I have confirmed that your approach was indeed correct for generating the SOAP reponse. I'm editing this section to reflect simpler code that still works.
In the Javascript Writer script
var barcode = channelMap.get('ack_msg');
var mirthResponse = ResponseFactory.getSuccessResponse(barcode);
responseMap.put('Barcode', mirthResponse);
Thank you very much csj,
I played around and got mine to work and looking at your solution, you pointed out my bottle neck to the issue as well which is the XML part, I did not realize you have to cast it into XML as per the new XML when you already call toXML function :)
Here is my script, though basic I thought I post it up for anyone find it useful down the road.
var destination = responseMap.get('QueryToVista');
var responseMessage = destination.getMessage();
var Xmsg = new XML(SerializerFactory.getHL7Serializer().toXML(responseMessage));
var xml_msg = '<?xml version="1.0" encoding="utf-8" ?>'+
'<XML><Patient Name="'+Xmsg['PID']['PID.5']['PID.5.1']+
'" Barcode="'+Xmsg['QRD']['QRD.8']['QRD.8.1']+'" /></XML>';
var sResp = ResponseFactory.getSuccessResponse(xml_msg)
responseMap.put('Response', sResp);

What's the best way to handle a REST API's 'create' response in Backbone.js

I'm using backbone.js to interact with a REST API that, when posting to it to create a new resource, responds with a status of 201, a 'Location' header pointing to the resource's URI, but an empty body.
When I create a new model at the moment, its successful, but the local representation of the model only contains the properties I explicitly set, not any of the properties that would be set on the server (created_date, etc.)
From what I understand, Backbone would update its representation of the model with data in the body, if there were any. But, since there isn't, it doesn't.
So, clearly, I need to use the location in the Location header to update the model, but what's the best way to do this.
My current mindset is that I would have to parse the url from the header, split out the id, set the id for the model, then tell the model to fetch().
This seems really messy. Is there a cleaner way to do it?
I have some influence over the API. Is the best solution to try to get the API author to return the new model as the body of the response (keeping the 201 and the location header as well)?
Thanks!
Sounds like you will have to do a little customization.
Perhaps override the parse method and url method of your model class inherited from
Backbone.Model.
The inherited functions are:
url : function() {
var base = getUrl(this.collection);
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
},
parse : function(resp) {
return resp;
},
and you could try something like:
parse: function(resp, xhr) {
this._url = xhr.getResponseHeader('location')
return resp
}
url: function() {
return this._url
}
Yes, backbone.js really wants the result of a save (be it PUT or POST) to be a parseable body which can be used to update the model. If, as you say, you have influence over the API, you should see if you can arrange for the content body to contain the resource attributes.
As you point out, its makes little sense to make a second over-the-wire call to fully materialize the model.
It may be that a status code of 200 is more appropriate. Purists may believe that a 201 status code implies only a location is returned and not the entity. Clearly, that doesn't make sense in this case.
With Backbone 0.9.9, I couldn't get the accepted answer to work. The signature of the parse function seems to have changed in an older version, and the xhr object is no longer available in the function signature.
This is an example of what I did, to make it work with Backbone v0.9.9 and jQuery 1.8.3 (using a Deferred Object/Promise), relying on the jqXHR object returned by Backbone.Model.save() :
window.CompanyView = Backbone.View.extend({
// ... omitted other functions...
// Invoked on a form submit
createCompany: function(event) {
event.preventDefault();
// Store a reference to the model for use in the promise
var model = this.model;
// Backbone.Model.save returns a jqXHR object
var xhr = model.save();
xhr.done(function(resp, status, xhr) {
if (!model.get("id") && status == "success" && xhr.status == 201) {
var location = xhr.getResponseHeader("location");
if (location) {
// The REST API sends back a Location header of format http://foo/rest/companys/id
// Split and obtain the last fragment
var fragments = location.split("/");
var id = fragments[fragments.length - 1];
// Set the id attribute of the Backbone model. This also updates the id property
model.set("id", id);
app.navigate('companys/' + model.id, {trigger: true});
}
}
});
}
});
I did not use the success callback that could be specified in the options hash provided to the Backbone.Model.save function, since that callback is invoked before the XHR response is received. That is, it is pointless to store a reference to the jqXHR object and use it in the success callback, since the jqXHR would not contain any response headers (yet) when the callback is invoked.
Another other to solve this would be to write a custom Backbone.sync implementation, but I didn't prefer this approach.