Browse Source

Merge pull request #4187 from afishbeck/wsecl_json_url

HPCC-8799 WsEcl support for JSON via HTTP-GET and FORM-ENCODED-POST

Reviewed-By: Gavin Halliday <gavin.halliday@lexisnexis.com>
Reviewed-By: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 12 years ago
parent
commit
5b23a8d702

+ 45 - 28
esp/services/ws_ecl/ws_ecl_service.cpp

@@ -1744,19 +1744,16 @@ void CWsEclBinding::getWsEcl2XmlRequest(StringBuffer& soapmsg, IEspContext &cont
     }
 }
 
-StringBuffer &appendJSONException(StringBuffer &s, IException *e, const char *objname="Exceptions", const char *arrayName = "Exception")
+StringBuffer &appendJSONExceptionItem(StringBuffer &s, int code, const char *msg, const char *objname="Exceptions", const char *arrayName = "Exception")
 {
-    if (!e)
-        return s;
     if (objname && *objname)
         appendJSONName(s, objname).append('{');
     if (arrayName && *arrayName)
         appendJSONName(s, arrayName).append('[');
     delimitJSON(s);
     s.append('{');
-    appendJSONValue(s, "Code", e->errorCode());
-    StringBuffer temp;
-    appendJSONValue(s, "Message", e->errorMessage(temp).str());
+    appendJSONValue(s, "Code", code);
+    appendJSONValue(s, "Message", msg);
     s.append('}');
     if (arrayName && *arrayName)
         s.append(']');
@@ -1765,6 +1762,14 @@ StringBuffer &appendJSONException(StringBuffer &s, IException *e, const char *ob
     return s;
 }
 
+StringBuffer &appendJSONException(StringBuffer &s, IException *e, const char *objname="Exceptions", const char *arrayName = "Exception")
+{
+    if (!e)
+        return s;
+    StringBuffer temp;
+    return appendJSONExceptionItem(s, e->errorCode(), e->errorMessage(temp).str(), objname, arrayName);
+}
+
 StringBuffer &appendJSONExceptions(StringBuffer &s, IMultiException *e, const char *objname="Exceptions", const char *arrayName = "Exception")
 {
     if (!e)
@@ -1830,29 +1835,29 @@ void CWsEclBinding::getWsEclJsonResponse(StringBuffer& jsonmsg, IEspContext &con
         element.append(wsinfo.queryname.sget());
         element.append("Response");
 
-        VStringBuffer xpath("Body/%s/Results/Result/Exception", element.str());
-        Owned<IPropertyTreeIterator> exceptions = parmtree->getElements(xpath.str());
+        IPropertyTree *node = parmtree;
+        if (node->hasProp("Body"))
+            node = node->queryPropTree("Body");
+        if (node->hasProp(element))
+            node = node->queryPropTree(element);
+        if (node->hasProp("Results"))
+            node = node->queryPropTree("Results");
+        if (node->hasProp("Result"))
+            node = node->queryPropTree("Result");
 
         jsonmsg.appendf("{\n  \"%s\": {\n    \"Results\": {\n", element.str());
 
+        Owned<IPropertyTreeIterator> exceptions = node->getElements("Exception");
         if (exceptions && exceptions->first())
         {
-            jsonmsg.append("      \"Exceptions\": {\n        \"Exception\": [\n");
-            bool first=true;
+            appendJSONName(jsonmsg.pad(3), "Exceptions").append("{\n");
+            appendJSONName(jsonmsg.pad(4), "Exception").append("[\n");
             ForEach(*exceptions)
-            {
-                if (first)
-                    first=false;
-                else
-                    jsonmsg.append(",\n");
-            jsonmsg.appendf("          {\n            \"Code\": %d,\n            \"Message\": \"%s\"\n          }", exceptions->query().getPropInt("Code"), exceptions->query().queryProp("Message"));
-            }
-            jsonmsg.append("\n        ]\n      }\n");
+                appendJSONExceptionItem(jsonmsg.pad(2), exceptions->query().getPropInt("Code"), exceptions->query().queryProp("Message"), NULL, NULL);
+            jsonmsg.append("\n   ]\n    }\n");
         }
 
-        xpath.clear().append("Body/*[1]/Results/Result/Dataset");
-        Owned<IPropertyTreeIterator> datasets = parmtree->getElements(xpath.str());
-
+        Owned<IPropertyTreeIterator> datasets = node->getElements("Dataset");
         ForEach(*datasets)
         {
             IPropertyTree &ds = datasets->query();
@@ -1878,7 +1883,6 @@ void CWsEclBinding::getWsEclJsonResponse(StringBuffer& jsonmsg, IEspContext &con
                 }
             }
         }
-
         jsonmsg.append("    }\n  }\n}");
     }
     catch (IException *e)
@@ -2382,8 +2386,10 @@ void CWsEclBinding::sendRoxieRequest(const char *target, StringBuffer &req, Stri
     }
 }
 
-int CWsEclBinding::onSubmitQueryOutputXML(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo, const char *format)
+int CWsEclBinding::onSubmitQueryOutput(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo, const char *format)
 {
+    bool outputJSON = !format ? false : strieq(format, "json");
+
     StringBuffer soapmsg;
 
     getSoapMessage(soapmsg, context, request, wsinfo, REQXML_TRIM|REQXML_ROOT);
@@ -2407,17 +2413,28 @@ int CWsEclBinding::onSubmitQueryOutputXML(IEspContext &context, CHttpRequest* re
         StringBuffer roxieresp;
         sendRoxieRequest(wsinfo.qsetname.get(), soapmsg, roxieresp, status, wsinfo.queryname);
 
-        Owned<IWuWebView> web = createWuWebView(*wsinfo.wu, wsinfo.queryname.get(), getCFD(), true);
-        if (web.get())
-            web->expandResults(roxieresp.str(), output, xmlflags);
+        if (outputJSON)
+            getWsEclJsonResponse(output, context, request, roxieresp.str(), wsinfo);
+        else
+        {
+            Owned<IWuWebView> web = createWuWebView(*wsinfo.wu, wsinfo.queryname.get(), getCFD(), true);
+            if (web.get())
+                web->expandResults(roxieresp.str(), output, xmlflags);
+        }
     }
     else
     {
         submitWsEclWorkunit(context, wsinfo, soapmsg.str(), output, xmlflags);
+        if (outputJSON)
+        {
+            StringBuffer jsonresp;
+            getWsEclJsonResponse(jsonresp, context, request, output.str(), wsinfo);
+            output.swapWith(jsonresp);
+        }
     }
 
     response->setContent(output.str());
-    response->setContentType(HTTP_TYPE_APPLICATION_XML);
+    response->setContentType(outputJSON ? "application/json" : "application/xml");
     response->setStatus("200 OK");
     response->send();
 
@@ -2725,7 +2742,7 @@ int CWsEclBinding::onGet(CHttpRequest* request, CHttpResponse* response)
             nextPathNode(thepath, format);
 
             WsEclWuInfo wsinfo(wuid.str(), qs.str(), qid.str(), context->queryUserId(), context->queryPassword());
-            return onSubmitQueryOutputXML(*context, request, response, wsinfo, format.str());
+            return onSubmitQueryOutput(*context, request, response, wsinfo, format.str());
         }
         else if (!stricmp(methodName.str(), "xslt"))
         {

+ 1 - 1
esp/services/ws_ecl/ws_ecl_service.hpp

@@ -165,7 +165,7 @@ public:
     void buildSampleResponseXml(StringBuffer& msg, IEspContext &context, CHttpRequest* request, WsEclWuInfo &wsinfo);
     void getSoapMessage(StringBuffer& soapmsg, IEspContext &context, CHttpRequest* request, WsEclWuInfo &wsinfo, unsigned flags);
     int onGetSoapBuilder(IEspContext &context, CHttpRequest* request, CHttpResponse* response,  WsEclWuInfo &wsinfo);
-    int onSubmitQueryOutputXML(IEspContext &context, CHttpRequest* request, CHttpResponse* response,    WsEclWuInfo &wsinfo, const char *format);
+    int onSubmitQueryOutput(IEspContext &context, CHttpRequest* request, CHttpResponse* response,    WsEclWuInfo &wsinfo, const char *format);
     int onSubmitQueryOutputView(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo);
 
     int submitWsEclWorkunit(IEspContext & context, WsEclWuInfo &wsinfo, const char *xml, StringBuffer &out, unsigned flags, const char *viewname=NULL, const char *xsltname=NULL);

+ 8 - 5
esp/xslt/wsecl3_form.xsl

@@ -156,7 +156,9 @@ function setESPFormAction()  // reqType: 0: regular form, 1: soap, 2: form param
         else if (actval.value=="run_xslt")
             actionpath = "]]></xsl:text><xsl:value-of disable-output-escaping="yes"  select="concat('/WsEcl/xslt/query/', $queryPath, '/', $methodName)"/><xsl:text disable-output-escaping="yes"><![CDATA[";
         else if (actval.value=="xml")
-            actionpath = "]]></xsl:text><xsl:value-of disable-output-escaping="yes"  select="concat('/WsEcl/submit/query/', $queryPath, '/', $methodName, '?view=xml&amp;display')"/><xsl:text disable-output-escaping="yes"><![CDATA[";
+            actionpath = "]]></xsl:text><xsl:value-of disable-output-escaping="yes"  select="concat('/WsEcl/submit/query/', $queryPath, '/', $methodName, '/xml?view=xml&amp;display')"/><xsl:text disable-output-escaping="yes"><![CDATA[";
+        else if (actval.value=="json")
+            actionpath = "]]></xsl:text><xsl:value-of disable-output-escaping="yes"  select="concat('/WsEcl/submit/query/', $queryPath, '/', $methodName, '/json')"/><xsl:text disable-output-escaping="yes"><![CDATA[";
         else
             actionpath= "]]></xsl:text><xsl:value-of disable-output-escaping="yes"  select="concat('/WsEcl/xslt/query/', $queryPath, '/', $methodName, '?view=')"/><xsl:text disable-output-escaping="yes"><![CDATA["+actval.value;
     }
@@ -308,10 +310,11 @@ function switchInputForm()
                         <xsl:for-each select="/FormInfo/CustomViews/Result">
                             <option><xsl:attribute name="value"><xsl:value-of select="."/></xsl:attribute><xsl:value-of select="."/></option>
                         </xsl:for-each>
-                        <option value="run_xslt">OUTPUT TABLES</option>
-                        <option value="xml">OUTPUT XML</option>
-                        <option value="esp_soap">SOAP TEST</option>
-                        <option value="esp_json">JSON TEST</option>
+                        <option value="run_xslt">Output Tables</option>
+                        <option value="xml">Output XML</option>
+                        <option value="json">Output JSON</option>
+                        <option value="esp_soap">SOAP Test</option>
+                        <option value="esp_json">JSON Test</option>
                     </select>&nbsp;
                    <input type='submit' value='Submit' name='S1'/>