Creating an api in Cache

779 views
Skip to first unread message

Roberto

unread,
Oct 30, 2013, 2:35:46 PM10/30/13
to intersystems...@googlegroups.com
Can people in this group provide best practices for creating an api (REST) in Cache/CSP?

Thanks for your responses.

-Roberto

Tom Fitzgibbon

unread,
Oct 30, 2013, 2:53:04 PM10/30/13
to intersystems...@googlegroups.com
Roberto -

Below might help, I just got something going with a jQuery mobile autocomplete.  Disclaimer: I'm not an expert in REST/Cache yet, so it's not necessarily a best practice example.

The CSP page below calls the class, which reads the Cache Global, repackages data in JSON and returns in the AJAX call.

Tom Fitzgibbon | Multidata | 212-967-6700 x537

Class Supplying JSON data from Cache Global ------------------------------
Class HD2.TKFREST1 Extends %CSP.Page
{

ClassMethod OnPage() As %Status [ ServerOnly = 1 ]
{
 s RDcallback = %request.Data("callback",1)
 s RDq=%request.Data("q",1)
 
  ;Make JSON from global
 s ResJSON=..MakeJSON(RDq),x=RDcallback_ResJSON
 s ^TKF=x
 
 w x
 Q $$$OK
}

ClassMethod MakeJSON(RDq) As %String [ ServerOnly = 1 ]
{
 ;select appropriate globals from ^TKFC and package in JSONP
 s ResJSON="([",city="",URDq=$zconvert(RDq,"U")
 f  s city=$o(^TKFC(city)) q:'$l(city)  i city[URDq s ResJSON=ResJSON_""""_^TKFC(city)_""""_","
 s ResJSON=$e(ResJSON,1,$l(ResJSON)-1)
 s ResJSON=ResJSON_"]);"
 Q ResJSON
}

ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ]
{
 Do %response.SetHeader("Content-Type","text/json")
 Q 1
}
}

CSP Page Providing Autocomplete that consumes JSON data from Class Above ----------------------------
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TKF - Listview Autocomplete - jQuery Mobile Demos</title>

    <script language="javascript">
       
$( document ).on( "pageinit", "#myPage", function() {
$( "#autocomplete" ).on( "listviewbeforefilter", function ( e, data ) {
var $ul = $( this ),
$input = $( data.input ),
value = $input.val(),
html = "";
$ul.html( "" );
if ( value && value.length > 2 ) {
$ul.html( "<li><div class='ui-loader'><span class='ui-icon ui-icon-loading'></span></div></li>" );
$ul.listview( "refresh" );
$.ajax({
dataType: "jsonp",
crossDomain: true,
data: {
q: $input.val()
}
})
.then( function ( response ) {
$.each( response, function ( i, val ) {
html += '<li class="city" id="city_' + val.id + '"">' + val + "</li>";
});
$ul.html( html );
$ul.listview( "refresh" );
$ul.trigger( "updatelayout");
$('.city').click(function(){
     var val=$(this).html();
    alert(val); //do something
    $('.city').panel('close');
    });
});
}
});
});
</script>
<style>
.ui-listview-filter-inset {
margin-top: 0;
}
    </style>
</head>
<body>
<div data-role="page" id="myPage">

<div data-role="header" data-theme="a">
<h1>Listview Autocomplete</h1>
<a href="../" data-icon="home" data-iconpos="notext" data-ajax="false">Home</a>
</div><!-- /header -->

<div data-role="content">

<div class="content-primary">
        
  <h2>Remote autocomplete with listview filter</h2>

<div data-demo-html="true" data-demo-js="true" data-demo-css="true">
<h3>Cities worldwide</h3>
            <p>After you enter <strong>at least three characters</strong> the autocomplete function will show all possible matches.</p> 
<ul id="autocomplete" data-role="listview" data-inset="true" data-filter="true" data-filter-placeholder="Find a city..." data-filter-theme="a"></ul>
</div><!--/demo-html -->


</div><!--/content-primary -->

</div><!-- /content -->
<div class="json test">
</div>

<div class="jqm-footer">
<p class="jqm-version"></p>
<p>&nbsp;&nbsp;&copy; 2013 open source stuff - TKF</p>
</div><!-- /jqm-footer -->

</div><!-- /page -->

</body>
</html>



--
--
Caché, Ensemble, DeepSee
 
---
You received this message because you are subscribed to the Google Groups "Caché, Ensemble, DeepSee" group.
To unsubscribe from this group and stop receiving emails from it, send an email to intersystems-publi...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Roberto

unread,
Oct 30, 2013, 3:19:16 PM10/30/13
to intersystems...@googlegroups.com
Hello Tom,

Thanks for the example. I can use this in the future!

What about things like security?

I know for something like Google maps I need an api key. Have you (or anyone here) implemented something like that?

It should be pretty easy to do, but I've never done it in Cache.

Also what about licensing? Any issue with that?

Thanks,

-Roberto


To unsubscribe from this group and stop receiving emails from it, send an email to intersystems-public-cache+unsub...@googlegroups.com.

Tom Fitzgibbon

unread,
Oct 30, 2013, 3:27:55 PM10/30/13
to intersystems...@googlegroups.com
Roberto - Don't have answers for security and licensing yet.  Let's keep talking about these and other issues.

I'm also trying to figure out if Cache 2014 has some Zen.JSON stuff to read Cache tables and return usable JSON, but as Bill M. points out different jQuery components want data back in different JSON formats.  We keep running into Tower of Babel problem with formats, languages, libraries, etc.

 - Tom


To unsubscribe from this group and stop receiving emails from it, send an email to intersystems-publi...@googlegroups.com.

Roberto

unread,
Oct 30, 2013, 3:31:52 PM10/30/13
to intersystems...@googlegroups.com
Yes, we are very interested in this.

We realize that a creating an api library like this is a lot simpler than creating web services. But we need to address a lot of questions first.

I saw your posting about the different json format, but was not quite sure about the issue.

Can you post the 2 different sample jsons? Maybe I can help you with that.

-Roberto
To unsubscribe from this group and stop receiving emails from it, send an email to intersystems-public-cache+unsubsc...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.

DAiMor

unread,
Oct 30, 2013, 4:47:28 PM10/30/13
to intersystems...@googlegroups.com
Now, you can create your REST API in versions from 2014.1.

This version introduces support for handling REST requests, routing them to a given namespace, and processing them.
This support is at the level of the CSP Gateway.
This feature is predicated on providing a Web application that routes all requests to a given handler class that, based on a
URL mapping facility, then dispatches the calls to the correct class and method implementation. With this feature, Caché
can now handle any REST interface; for data transport, Caché supports XML or JSON.


среда, 30 октября 2013 г., 22:35:46 UTC+4 пользователь Roberto написал:

Roberto

unread,
Oct 30, 2013, 4:51:34 PM10/30/13
to intersystems...@googlegroups.com
That's good to know!

Is 2014.1 release candidate out yet?

I thought they were still working on 2013.2?

-Roberto

Dmitry Maslennikov

unread,
Oct 30, 2013, 4:55:31 PM10/30/13
to intersystems...@googlegroups.com

They changed version 2013.2 to 2014.1 and this version in fieldtest yet.

31.10.2013 0:51 пользователь "Roberto" <rcah...@gmail.com> написал:
--
--
Caché, Ensemble, DeepSee
 
---
You received this message because you are subscribed to a topic in the Google Groups "Caché, Ensemble, DeepSee" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/intersystems-public-cache/I1JiuzAZqP4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to intersystems-publi...@googlegroups.com.

rtweed

unread,
Oct 31, 2013, 7:57:08 AM10/31/13
to intersystems...@googlegroups.com
Rather than use CSP, you can use EWD.js (previously known as EWD Lite)


  • Specifically see the chapter titled "Web Service Interface"
Rob

Roberto

unread,
Oct 31, 2013, 8:44:58 AM10/31/13
to intersystems...@googlegroups.com
Hi Rob,

I've checked out EWD before (read docs/pdf, went to the website). It looks like with EWD you need node.js running with Cache. This is another layer that we are not familiar with.

Maybe during the holiday break (Thanksgiving, U.S.) I will take a deeper look at EWD and see how we can implement it.

Thank you.

-Roberto

rtweed

unread,
Oct 31, 2013, 10:37:38 AM10/31/13
to intersystems...@googlegroups.com
That's correct - you need Node.js, but on the other hand you dispense with CSP and Apache/IIS.  Plus the web services you expose are automatically secured via the same mechanism used by Amazon Web Services.

Anyone who's serious about browser-based applications these days needs to understand JavaScript, and that's what Node.js is: JavaScript on the server.  In fact, IMO, everyone should be finding out about Node.js.

I recommend you try it out with Mike Clayton's Linux installer - if you're an Amazon EC2 user, you can have a fully-working environment (based on GlobalsDB) up and running in about 10 minutes.  See the Appendix in the EWD.js documentation.  Everything that applies to GlobalsDB applies to Cache, except that with Cache you have the added advantage of being able to invoke Cache code from within Node.js

Rob
Message has been deleted

Tony

unread,
Nov 3, 2013, 11:02:44 AM11/3/13
to intersystems...@googlegroups.com
Hi Roberto

Another approach to creating a web API into Caché is to use the CSP session key to encrypt the various server methods a client can call, see example below.  The list build values are event class, method and number of parameters.  The downside to this approach is you have to define all the web methods that could be called, but is does mean you hide the implementation from the user (i.e. URL path).

<script type="text/javascript">
// Create a new global object for storing data and functions
var cspPage = {
appTimeout: #(%session.AppTimeout)#,
serverHandle: '#($Select(%session.UseSessionCookie '= 2: %session.CSPSessionCookie, 1: ""))#', 
serverMethod: {
save: '#(..Encrypt($ListBuild("User.Models.Person", "Save", 0)))#'
}
};
</script>

You then have a single entry point back into Caché (e.g. 'User.Controllers.HomeController.cls') that decrypts the web event (e.g. 'serverMethod.save') which then calls the appropriate class method (e.g. 'Save' in 'User.Models.Person'), see example below.

save: function () {
var qString = '?WEVENT=' + cspPage.serverMethod.save;
if (cspPage.serverHandle != '') { qString += '&CSPCHD=' + cspPage.serverHandle; }
//qString += '&WARG_1=' + someArg;
$.ajax({
url: "User.Controllers.HomeController.cls" + qString, 
type: "post", 
data: ko.toJSON(this), 
contentType: "application/json", 
success: function (result) {
cspPage.fn.resetTimeout();  // reset page timeout
console.log('result.message=', result.message);
$('#myModal div.modal-body p').html('<p>' + result.message + '</p>');
$('#myModal').modal({
show: true
});
}
});
}

On the server-side it is assumed JSON data is being exchanged, see example below.

Class User.Controllers.HomeController Extends %CSP.Page
{

Parameter CONTENTTYPE = "application/json";

ClassMethod OnPage() As %Status
{
// chunk the output
Set stream = %session.Data("json")
Do stream.Rewind()
While 'stream.AtEnd {
Set length = 1024
Write stream.Read(.length)
}
// end of method
Quit $$$OK
}

ClassMethod OnPreHTTP() As %Boolean
{
Set ok = 1
Try {
// get requested event
Set evtData = $Get(%request.Data("WEVENT", 1))
If evtData = "" ZTrap
Set evtData = ..Decrypt(evtData)
Set evtClss = $ListGet(evtData, 1)
Set evtMthd = $ListGet(evtData, 2)
Set evtArgs = $ListGet(evtData, 3)
// define json data from client
Set jsonData = %request.Content
Set sc = jsonData.Rewind()
// get the required parameters and invoke appropriate class method
For i = 1:1:evtArgs Set evtArgs(i) = $Get(%request.Data("WARG_"_i, 1))
If 'evtArgs Set jsonBack = $ClassMethod(evtClss, evtMthd, jsonData)
If evtArgs = 1 Set jsonBack = $ClassMethod(evtClss, evtMthd, jsonData, evtArgs(1))
If evtArgs = 2 Set jsonBack = $ClassMethod(evtClss, evtMthd, jsonData, evtArgs(1), evtArgs(2))
If evtArgs = 3 Set jsonBack = $ClassMethod(evtClss, evtMthd, jsonData, evtArgs(1), evtArgs(2), evtArgs(3))
// add json stream oref to session data
Set %session.Data("json") = jsonBack
} Catch exception {
Set ok = 0
Do BACK^%ETN
Set %response.Status = "500 Internal Server Error"
}
// end of method
Quit ok
}

}

I have used the above code whilst experimenting with the 'Model-View-View Model (MVVM)' design pattern based on the 'KnockoutJS (http://knockoutjs.com/)' framework.  I know the above mechanism is not really RESTful, but some may find it meets their needs.

Tony
Reply all
Reply to author
Forward
0 new messages