From 17b49b1f8e6abd510a67ee27dc777b2e468feeb1 Mon Sep 17 00:00:00 2001 From: Terrence Asselin Date: Tue, 4 Jun 2024 11:49:32 -0500 Subject: [PATCH] HPCC-31936 Fix WsEcl sample XML, WSDL and XSD features Adjust EspHttpBinding and CWsEclBinding to use info from the roxie to generate WSDL/XSD and sample request/response XML rather than attempting to use ESDL. This means moving the old implmenetation of several functions from prior to HPCC-28978 from EspHttpBinding into CWsEclBinding. Specifically: - getSchema is overridden in CWsEclBinding, and called when the ESDL-based generation isn't applicable (there is no xmlServiceFileName). - getWsdlOrXsd is moved into CWsEclBinding, and the onXSD/onWsdl handlers in CWsEclBinding call it directly. Noted that the non-overridden CWsEclBinding member function getSchema was substantially similar to the overridden version, so renamed it getSimpleSchema and made it protected. Created ticket HPCC-32016 to refactor that code. Signed-off-by: Terrence Asselin --- esp/bindings/http/platform/httpbinding.cpp | 9 +- esp/services/ws_ecl/ws_ecl_service.cpp | 178 ++++++++++++++++++++- esp/services/ws_ecl/ws_ecl_service.hpp | 8 +- 3 files changed, 183 insertions(+), 12 deletions(-) diff --git a/esp/bindings/http/platform/httpbinding.cpp b/esp/bindings/http/platform/httpbinding.cpp index 884eb0d967f..c5a9e831844 100644 --- a/esp/bindings/http/platform/httpbinding.cpp +++ b/esp/bindings/http/platform/httpbinding.cpp @@ -1764,7 +1764,9 @@ void EspHttpBinding::getServiceSchema(IEspContext& context, CHttpRequest* reque StringBuffer xmlFilename; if (!getServiceXmlFilename(xmlFilename)) { - throw MakeStringException(-1, "Unable to get service XML filename"); + // Allow subclassed specialized implementation that doesn't use ESDL + getSchema(schema, context, request, serviceQName, methodQName, true); + return; } StringBuffer nstr; @@ -1841,8 +1843,7 @@ int EspHttpBinding::getServiceWsdlOrXsd(IEspContext &context, CHttpRequest* requ } StringBuffer schema; - getServiceSchema(context, request, serviceQName, methodQName, - version, isWsdl, false, schema); + getServiceSchema(context, request, serviceQName, methodQName, version, isWsdl, false, schema); response->setContent(schema.length(), schema.str()); response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8); @@ -1937,7 +1938,7 @@ void EspHttpBinding::generateSampleXml(bool isRequest, IEspContext &context, CHt content.appendf("generateSampleXml schema error: %s::%s", serv, method); return; } - + getServiceSchema(context, request, serviceQName, methodQName, getVersion(context), false, false, schemaXml); Owned schema; diff --git a/esp/services/ws_ecl/ws_ecl_service.cpp b/esp/services/ws_ecl/ws_ecl_service.cpp index 9a2db171a3d..6856305cc8e 100644 --- a/esp/services/ws_ecl/ws_ecl_service.cpp +++ b/esp/services/ws_ecl/ws_ecl_service.cpp @@ -1403,7 +1403,7 @@ int CWsEclBinding::getXsdDefinition(IEspContext &context, CHttpRequest *request, } -bool CWsEclBinding::getSchema(StringBuffer& schema, IEspContext &ctx, CHttpRequest* req, WsEclWuInfo &wsinfo) +bool CWsEclBinding::getSimpleSchema(StringBuffer& schema, IEspContext &ctx, CHttpRequest* req, WsEclWuInfo &wsinfo) { Owned namespaces = createPTree(); appendSchemaNamespaces(namespaces, ctx, req, wsinfo); @@ -1453,6 +1453,94 @@ bool CWsEclBinding::getSchema(StringBuffer& schema, IEspContext &ctx, CHttpReque return true; } +// Moved from the prior implementation in EspHttpBinding which now relies on ESDL to generate the schema. +// However, since WsEcl is acting a front-end for roxie queries, it needs this custom implementation +// that uses the information from the roxie instead of ESDL. +bool CWsEclBinding::getSchema(StringBuffer& schema, IEspContext &ctx, CHttpRequest* req, const char *service, const char *method,bool standalone) +{ + StringBuffer serviceQName; + StringBuffer methodQName; + + if (!qualifyServiceName(ctx, service, method, serviceQName, &methodQName)) + return false; + + const char *sqName = serviceQName.str(); + const char *mqName = methodQName.str(); + + Owned namespaces = createPTree(); + appendSchemaNamespaces(namespaces, ctx, req, service, method); + Owned nsiter = namespaces->getElements("namespace"); + + StringBuffer nstr; + generateNamespace(ctx, req, sqName, mqName, nstr); + schema.appendf("query(); + schema.appendf(" xmlns:%s=\"%s\"", ns.queryProp("@nsvar"), ns.queryProp("@ns")); + } + schema.append(">\n"); + ForEach(*nsiter) + { + IPropertyTree &ns = nsiter->query(); + if (ns.hasProp("@import")) + schema.appendf("", ns.queryProp("@ns"), ns.queryProp("@location")); + } + + + schema.append( + "" + "" + "" + "" + "" + "" + "" + "\n" + "" + "" + "" + "" + "" + "\n" + "\n" + ); + + if (ctx.queryOptions()&ESPCTX_WSDL_EXT) + { + schema.append( + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "\n" + ); + } + + bool mda=(req->queryParameters()->getPropInt("mda")!=0); + getXsdDefinition(ctx, req, schema, sqName, mqName, mda); + schema.append("\n"); + schema.append(""); + return true; +} + int CWsEclBinding::getGenForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wuinfo, bool box) { IConstWorkUnit *wu = wuinfo.ensureWorkUnit(); @@ -1513,7 +1601,7 @@ int CWsEclBinding::getGenForm(IEspContext &context, CHttpRequest* request, CHttp } } else - getSchema(formxml, context, request, wuinfo); + getSimpleSchema(formxml, context, request, wuinfo); formxml.append(""); if (web) @@ -1621,7 +1709,7 @@ void CWsEclBinding::getWsEcl2XmlRequest(StringBuffer& soapmsg, IEspContext &cont element.append(wsinfo.queryname.str()).append("Request"); StringBuffer schemaXml; - getSchema(schemaXml, context, request, wsinfo); + getSimpleSchema(schemaXml, context, request, wsinfo); ESPLOG(LogMax,"request schema: %s", schemaXml.str()); Owned schema = createXmlSchemaFromString(schemaXml); if (schema.get()) @@ -1657,7 +1745,7 @@ void CWsEclBinding::getWsEclJsonRequest(StringBuffer& jsonmsg, IEspContext &cont element.append("Request"); StringBuffer schemaXml; - getSchema(schemaXml, context, request, wsinfo); + getSimpleSchema(schemaXml, context, request, wsinfo); ESPLOG(LogMax,"request schema: %s", schemaXml.str()); Owned schema = createXmlSchemaFromString(schemaXml); if (schema.get()) @@ -2290,7 +2378,7 @@ int CWsEclBinding::getWsdlBindings(IEspContext &context, CHttpRequest *request, int CWsEclBinding::onGetWsdl(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo) { context.setBindingValue(&wsinfo); - EspHttpBinding::onGetWsdl(context, request, response, wsinfo.qsetname.str(), wsinfo.queryname.str()); + getWsdlOrXsd(context, request, response, wsinfo.qsetname.str(), wsinfo.queryname.str(), true); context.setBindingValue(NULL); return 0; } @@ -2298,12 +2386,88 @@ int CWsEclBinding::onGetWsdl(IEspContext &context, CHttpRequest* request, CHttpR int CWsEclBinding::onGetXsd(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo) { context.setBindingValue(&wsinfo); - EspHttpBinding::onGetXsd(context, request, response, wsinfo.qsetname.str(), wsinfo.queryname.str()); + getWsdlOrXsd(context, request, response, wsinfo.qsetname.str(), wsinfo.queryname.str(), false); context.setBindingValue(NULL); return 0; } +int CWsEclBinding::getWsdlOrXsd(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method, bool isWsdl) +{ + bool mda=(request->queryParameters()->getPropInt("mda")!=0); + try + { + StringBuffer serviceQName; + StringBuffer methodQName; + + if (!qualifyServiceName(context, service, method, serviceQName, &methodQName)) + { + return onGetNotFound(context, request, response, service); + } + else + { + const char *sqName = serviceQName.str(); + const char *mqName = methodQName.str(); + StringBuffer ns; + generateNamespace(context, request, serviceQName.str(), methodQName.str(), ns); + + StringBuffer content(""); + if (context.queryRequestParameters()->hasProp("display")) + content.append(""); + else if (isWsdl && context.queryRequestParameters()->hasProp("wsdlviewer")) + content.append(""); + if (isWsdl) + { + content.appendf("", ns.str(), ns.str()); + content.append(""); + } + + getSchema(content,context,request,service,method,!isWsdl); + + if (isWsdl) + { + content.append(""); + + getWsdlMessages(context, request, content, sqName, mqName, mda); + getWsdlPorts(context, request, content, sqName, mqName, mda); + getWsdlBindings(context, request, content, sqName, mqName, mda); + + StringBuffer location(getWsdlAddress()); + if (request->queryParameters()->hasProp("wsdl_destination_path")) + location.append(request->queryParameters()->queryProp("wsdl_destination_path")); + else + location.append('/').append(sqName).appendf("?ver_=%g", context.getClientVersion()); + + if (request->queryParameters()->hasProp("encode_results")) + { + const char *encval = request->queryParameters()->queryProp("encode_results"); + location.append("&").appendf("encode_=%s", (encval && *encval) ? encval : "1"); + } + + content.appendf("", sqName); + content.appendf("", sqName, sqName); + content.appendf("", location.str()); + content.append(""); + content.append(""); + content.append(""); + } + + response->setContent(content.length(), content.str()); + response->setContentType(HTTP_TYPE_APPLICATION_XML_UTF8); + response->setStatus(HTTP_STATUS_OK); + } + } + catch (IException *e) + { + return onGetException(context, request, response, *e); + } + + response->send(); + return 0; +} + int CWsEclBinding::getWsEclDefinition(CHttpRequest* request, CHttpResponse* response, const char *thepath) { @@ -2423,7 +2587,7 @@ int CWsEclBinding::getRestURL(IEspContext *ctx, CHttpRequest *request, CHttpResp StringBuffer schemaXml; - getSchema(schemaXml, *ctx, request, wsinfo); + getSimpleSchema(schemaXml, *ctx, request, wsinfo); Owned schema = createXmlSchemaFromString(schemaXml); if (schema.get()) { diff --git a/esp/services/ws_ecl/ws_ecl_service.hpp b/esp/services/ws_ecl/ws_ecl_service.hpp index 8d3ce45e1ab..2a8e9edbe82 100644 --- a/esp/services/ws_ecl/ws_ecl_service.hpp +++ b/esp/services/ws_ecl/ws_ecl_service.hpp @@ -140,6 +140,12 @@ class CWsEclBinding : public CHttpSoapBinding private: CWsEclService *wsecl; +protected: + bool getSchema(StringBuffer& schema, IEspContext &ctx, CHttpRequest* req, const char *service, const char *method,bool standalone) override; + int getWsdlOrXsd(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *service, const char *method, bool isWsdl); + // Does not provide all the flexibility of the getSchema override. Consider refactoring out to use getSchema in its place. + bool getSimpleSchema(StringBuffer& schema, IEspContext &ctx, CHttpRequest* req, WsEclWuInfo &wsinfo) ; + public: CWsEclBinding(IPropertyTree *cfg, const char *bindname, const char *procname) : CHttpSoapBinding(cfg, bindname, procname), wsecl(NULL) @@ -199,7 +205,7 @@ class CWsEclBinding : public CHttpSoapBinding bool qualifyServiceName(IEspContext &context, const char *servname, const char *methname, StringBuffer &servQName, StringBuffer *methQName){servQName.clear().append(servname); if (methQName) methQName->clear().append(methname); return true;} int getXsdDefinition(IEspContext &context, CHttpRequest *request, StringBuffer &content, WsEclWuInfo &wsinfo); - bool getSchema(StringBuffer& schema, IEspContext &ctx, CHttpRequest* req, WsEclWuInfo &wsinfo) ; + void appendSchemaNamespaces(IPropertyTree *namespaces, IEspContext &ctx, CHttpRequest* req, WsEclWuInfo &wsinfo); void appendSchemaNamespaces(IPropertyTree *namespaces, IEspContext &ctx, CHttpRequest* req, const char *service, const char *method);