Skip to content

Commit

Permalink
HPCC-31936 Fix WsEcl sample XML, WSDL and XSD features
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
asselitx committed Jun 7, 2024
1 parent 6d078a4 commit 17b49b1
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 12 deletions.
9 changes: 5 additions & 4 deletions esp/bindings/http/platform/httpbinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1937,7 +1938,7 @@ void EspHttpBinding::generateSampleXml(bool isRequest, IEspContext &context, CHt
content.appendf("<Error>generateSampleXml schema error: %s::%s</Error>", serv, method);
return;
}

getServiceSchema(context, request, serviceQName, methodQName, getVersion(context), false, false, schemaXml);

Owned<IXmlSchema> schema;
Expand Down
178 changes: 171 additions & 7 deletions esp/services/ws_ecl/ws_ecl_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IPropertyTree> namespaces = createPTree();
appendSchemaNamespaces(namespaces, ctx, req, wsinfo);
Expand Down Expand Up @@ -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<IPropertyTree> namespaces = createPTree();
appendSchemaNamespaces(namespaces, ctx, req, service, method);
Owned<IPropertyTreeIterator> nsiter = namespaces->getElements("namespace");

StringBuffer nstr;
generateNamespace(ctx, req, sqName, mqName, nstr);
schema.appendf("<xsd:schema elementFormDefault=\"qualified\" targetNamespace=\"%s\" ", nstr.str());
if (standalone)
schema.appendf(" xmlns:tns=\"%s\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"", nstr.str());
ForEach(*nsiter)
{
IPropertyTree &ns = nsiter->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("<xsd:import namespace=\"%s\" schemaLocation=\"%s\"/>", ns.queryProp("@ns"), ns.queryProp("@location"));
}


schema.append(
"<xsd:complexType name=\"EspException\">"
"<xsd:all>"
"<xsd:element name=\"Code\" type=\"xsd:string\" minOccurs=\"0\"/>"
"<xsd:element name=\"Audience\" type=\"xsd:string\" minOccurs=\"0\"/>"
"<xsd:element name=\"Source\" type=\"xsd:string\" minOccurs=\"0\"/>"
"<xsd:element name=\"Message\" type=\"xsd:string\" minOccurs=\"0\"/>"
"</xsd:all>"
"</xsd:complexType>\n"
"<xsd:complexType name=\"ArrayOfEspException\">"
"<xsd:sequence>"
"<xsd:element name=\"Source\" type=\"xsd:string\" minOccurs=\"0\"/>"
"<xsd:element name=\"Exception\" type=\"tns:EspException\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>"
"</xsd:sequence>"
"</xsd:complexType>\n"
"<xsd:element name=\"Exceptions\" type=\"tns:ArrayOfEspException\"/>\n"
);

if (ctx.queryOptions()&ESPCTX_WSDL_EXT)
{
schema.append(
"<xsd:complexType name=\"EspSecurityInfo\">"
"<xsd:all>"
"<xsd:element name=\"UsernameToken\" minOccurs=\"0\">"
"<xsd:complexType>"
"<xsd:all>"
"<xsd:element name=\"Username\" minOccurs=\"0\"/>"
"<xsd:element name=\"Password\" minOccurs=\"0\"/>"
"</xsd:all>"
"</xsd:complexType>"
"</xsd:element>"
"<xsd:element name=\"RealmToken\" minOccurs=\"0\">"
"<xsd:complexType>"
"<xsd:all>"
"<xsd:element name=\"Realm\" minOccurs=\"0\"/>"
"</xsd:all>"
"</xsd:complexType>"
"</xsd:element>"
"</xsd:all>"
"</xsd:complexType>"
"<xsd:element name=\"Security\" type=\"tns:EspSecurityInfo\"/>\n"
);
}

bool mda=(req->queryParameters()->getPropInt("mda")!=0);
getXsdDefinition(ctx, req, schema, sqName, mqName, mda);
schema.append("<xsd:element name=\"string\" nillable=\"true\" type=\"xsd:string\" />\n");
schema.append("</xsd:schema>");
return true;
}

int CWsEclBinding::getGenForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wuinfo, bool box)
{
IConstWorkUnit *wu = wuinfo.ensureWorkUnit();
Expand Down Expand Up @@ -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("<CustomViews>");
if (web)
Expand Down Expand Up @@ -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<IXmlSchema> schema = createXmlSchemaFromString(schemaXml);
if (schema.get())
Expand Down Expand Up @@ -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<IXmlSchema> schema = createXmlSchemaFromString(schemaXml);
if (schema.get())
Expand Down Expand Up @@ -2290,20 +2378,96 @@ 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;
}

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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
if (context.queryRequestParameters()->hasProp("display"))
content.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/xmlformatter.xsl\"?>");
else if (isWsdl && context.queryRequestParameters()->hasProp("wsdlviewer"))
content.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/wsdl-viewer.xsl\"?>");
if (isWsdl)
{
content.appendf("<definitions xmlns=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" xmlns:http=\"http://schemas.xmlsoap.org/wsdl/http/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
" xmlns:mime=\"http://schemas.xmlsoap.org/wsdl/mime/\" xmlns:tns=\"%s\""
" targetNamespace=\"%s\">", ns.str(), ns.str());
content.append("<types>");
}

getSchema(content,context,request,service,method,!isWsdl);

if (isWsdl)
{
content.append("</types>");

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("&amp;").appendf("encode_=%s", (encval && *encval) ? encval : "1");
}

content.appendf("<service name=\"%s\">", sqName);
content.appendf("<port name=\"%sServiceSoap\" binding=\"tns:%sServiceSoap\">", sqName, sqName);
content.appendf("<soap:address location=\"%s\"/>", location.str());
content.append("</port>");
content.append("</service>");
content.append("</definitions>");
}

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)
{
Expand Down Expand Up @@ -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<IXmlSchema> schema = createXmlSchemaFromString(schemaXml);
if (schema.get())
{
Expand Down
8 changes: 7 additions & 1 deletion esp/services/ws_ecl/ws_ecl_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit 17b49b1

Please sign in to comment.