Jelajahi Sumber

HPCC-9917 Add webservice options to ECL

Add new FORMAT specifiers to STORED to affect field formatting and
sequence.

Add new #WEBSERVICE function for options affecting webservice.
Support explicit selection and ordering of specific fields.  Support
adding help and descriptive text.

Usage:

unsigned2 u1 := 0 : stored('u1', few, format(fieldwidth(8), sequence(12)));
integer2 i1 := 0 : stored('i11, format(fieldwidth(9), sequence(11)));

description("does cool stuff"));

Signed-off-by: Anthony Fishbeck <anthony.fishbeck@lexisnexis.com>
Anthony Fishbeck 10 tahun lalu
induk
melakukan
f8b44c6b03

+ 39 - 0
common/workunit/workunit.cpp

@@ -1945,12 +1945,14 @@ public:
     virtual IStringVal& getAttributeName(IStringVal &str) const;
     virtual IStringVal& getDefaultName(IStringVal &str) const;
     virtual IStringVal& getInfo(const char *name, IStringVal &str) const;
+    virtual IStringVal& getText(const char *name, IStringVal &str) const;
     virtual unsigned getWebServicesCRC() const;
  
     virtual void        setModuleName(const char *);
     virtual void        setAttributeName(const char *);
     virtual void        setDefaultName(const char *);
     virtual void        setInfo(const char *name, const char *info);
+    virtual void        setText(const char *name, const char *info);
     virtual void        setWebServicesCRC(unsigned);
 };
 
@@ -2022,6 +2024,8 @@ public:
     virtual unsigned    getResultHash() const;
     virtual bool        getResultIsAll() const;
     virtual IProperties *queryResultXmlns();
+    virtual IStringVal& getResultFieldOpt(const char *name, IStringVal &str) const;
+
 
     // interface IWUResult
     virtual void        setResultStatus(WUResultStatus status);
@@ -2055,6 +2059,7 @@ public:
     virtual void        setResultXML(const char *val);
     virtual void        setResultRow(unsigned len, const void * data);
     virtual void        setResultXmlns(const char *prefix, const char *uri);
+    virtual void        setResultFieldOpt(const char *name, const char *value);
 };
 
 class CLocalWUPlugin : public CInterface, implements IWUPlugin
@@ -5470,6 +5475,7 @@ void CLocalWorkUnit::copyWorkUnit(IConstWorkUnit *cached, bool all)
     copyTree(p, fromP, "Results");
     copyTree(p, fromP, "Graphs");
     copyTree(p, fromP, "Workflow");
+    copyTree(p, fromP, "WebServicesInfo");
     if (all)
     {
         // 'all' mode is used when setting up a dali WU from the embedded wu in a workunit dll
@@ -7693,6 +7699,11 @@ IStringVal& CLocalWUWebServicesInfo::getInfo(const char *name, IStringVal &str)
 
 }
 
+IStringVal& CLocalWUWebServicesInfo::getText(const char *name, IStringVal &str) const
+{
+    str.set(p->queryProp(name));
+    return str;
+}
 
 void CLocalWUWebServicesInfo::setModuleName(const char *mname)
 {
@@ -7722,6 +7733,11 @@ void CLocalWUWebServicesInfo::setInfo(const char *name, const char *info)
     p->setPropBin(name, m.length(), m.toByteArray());
 }
 
+void CLocalWUWebServicesInfo::setText(const char *name, const char *info)
+{
+    p->setProp(name, info);
+}
+
 
 //========================================================================================
 
@@ -8173,6 +8189,19 @@ IStringVal& CLocalWUResult::getResultFilename(IStringVal & str) const
     return str;
 }
 
+IStringVal& CLocalWUResult::getResultFieldOpt(const char *name, IStringVal &str) const
+{
+    str.clear();
+    if (!name || !*name)
+        return str;
+    IPropertyTree *format = p->queryPropTree("Format");
+    if (!format)
+        return str;
+    VStringBuffer xpath("@%s", name);
+    str.set(format->queryProp(xpath));
+    return str;
+}
+
 void CLocalWUResult::setResultStatus(WUResultStatus status)
 {
     setEnum(p, "@status", status, resultStatuses);
@@ -8199,6 +8228,16 @@ void CLocalWUResult::setResultXmlns(const char *prefix, const char *uri)
         xpath.append(':').append(prefix);
     p->setProp(xpath, uri);
 }
+
+void CLocalWUResult::setResultFieldOpt(const char *name, const char *value)
+{
+    if (!name || !*name)
+        return;
+    IPropertyTree *format = ensurePTree(p, "Format");
+    VStringBuffer xpath("@%s", name);
+    format->setProp(xpath, value);
+}
+
 void CLocalWUResult::setResultScalar(bool isScalar)
 {
     p->setPropInt("@isScalar", (int) isScalar);

+ 4 - 0
common/workunit/workunit.hpp

@@ -312,6 +312,7 @@ interface IConstWUResult : extends IInterface
     virtual void getResultDecimal(void * val, unsigned length, unsigned precision, bool isSigned) const = 0;
     virtual bool getResultIsAll() const = 0;
     virtual const IProperties *queryResultXmlns() = 0;
+    virtual IStringVal &getResultFieldOpt(const char *name, IStringVal &str) const = 0;
 };
 
 
@@ -348,6 +349,7 @@ interface IWUResult : extends IConstWUResult
     virtual void setResultXML(const char * xml) = 0;
     virtual void setResultRow(unsigned len, const void * data) = 0;
     virtual void setResultXmlns(const char *prefix, const char *uri) = 0;
+    virtual void setResultFieldOpt(const char *name, const char *value)=0;
 };
 
 
@@ -426,6 +428,7 @@ interface IConstWUWebServicesInfo : extends IInterface
     virtual IStringVal & getDefaultName(IStringVal & str) const = 0;
     virtual IStringVal & getInfo(const char * name, IStringVal & str) const = 0;
     virtual unsigned getWebServicesCRC() const = 0;
+    virtual IStringVal & getText(const char * name, IStringVal & str) const = 0;
 };
 
 
@@ -436,6 +439,7 @@ interface IWUWebServicesInfo : extends IConstWUWebServicesInfo
     virtual void setDefaultName(const char * pstr) = 0;
     virtual void setInfo(const char * name, const char * info) = 0;
     virtual void setWebServicesCRC(unsigned crc) = 0;
+    virtual void setText(const char * name, const char * text) = 0;
 };
 
 

+ 4 - 0
ecl/hql/hqlatoms.cpp

@@ -394,6 +394,7 @@ IAtom * stableAtom;
 IAtom * _state_Atom;
 IAtom * steppedAtom;
 IAtom * storedAtom;
+IAtom * storedFieldFormatAtom;
 IAtom * streamedAtom;
 IAtom * _streaming_Atom;
 IAtom * successAtom;
@@ -427,6 +428,7 @@ IAtom * virtualAtom;
 IAtom * _virtualSeq_Atom;
 IAtom * volatileAtom;
 IAtom * warningAtom;
+IAtom * webserviceAtom;
 IAtom * wholeAtom;
 IAtom * widthAtom;
 IAtom * wipeAtom;
@@ -823,6 +825,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKESYSATOM(state);
     MAKEATOM(stepped);
     MAKEATOM(stored);
+    MAKEATOM(storedFieldFormat);
     MAKEATOM(streamed);
     MAKESYSATOM(streaming);
     MAKEATOM(success);
@@ -857,6 +860,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKESYSATOM(virtualSeq);
     MAKEATOM(volatile);
     MAKEATOM(warning);
+    MAKEATOM(webservice);
     MAKEATOM(whole);
     MAKEATOM(width);
     MAKEATOM(wipe);

+ 2 - 0
ecl/hql/hqlatoms.hpp

@@ -397,6 +397,7 @@ extern HQL_API IAtom * stableAtom;
 extern HQL_API IAtom * _state_Atom;
 extern HQL_API IAtom * steppedAtom;
 extern HQL_API IAtom * storedAtom;
+extern HQL_API IAtom * storedFieldFormatAtom;
 extern HQL_API IAtom * streamedAtom;
 extern HQL_API IAtom * _streaming_Atom;
 extern HQL_API IAtom * successAtom;
@@ -431,6 +432,7 @@ extern HQL_API IAtom * virtualAtom;
 extern HQL_API IAtom * _virtualSeq_Atom;
 extern HQL_API IAtom * volatileAtom;
 extern HQL_API IAtom * warningAtom;
+extern HQL_API IAtom * webserviceAtom;
 extern HQL_API IAtom * wholeAtom;
 extern HQL_API IAtom * widthAtom;
 extern HQL_API IAtom * wipeAtom;

+ 26 - 1
ecl/hql/hqlgram.y

@@ -213,6 +213,7 @@ static void eclsyntaxerror(HqlGram * parser, const char * s, short yystate, int
   TOK_FIXED
   FLAT
   FROM
+  FORMAT
   FORMAT_ATTR
   FORWARD
   FROMJSON
@@ -552,6 +553,7 @@ static void eclsyntaxerror(HqlGram * parser, const char * s, short yystate, int
   HASH_STORED
   HASH_LINK
   HASH_ONWARNING
+  HASH_WEBSERVICE
 
   INTERNAL_READ_NEXT_TOKEN
 
@@ -1523,6 +1525,12 @@ setMetaCommand
                             }
                             $$.setExpr(createValue(no_setmeta, makeVoidType(), createAttribute(onWarningAtom), $3.getExpr(), $5.getExpr()), $1);
                         }
+    | HASH_WEBSERVICE '(' hintList ')'
+                        {
+                            HqlExprArray args;
+                            $3.unwindCommaList(args);
+                            $$.setExpr(createValue(no_setmeta, makeVoidType(), createExprAttribute(webserviceAtom, args)), $1);
+                        }
     ;
 
 hashStoredValue
@@ -1604,7 +1612,12 @@ failure
                             parser->normalizeExpression($5, type_string, true);
                             $$.setExpr(createValueF(no_persist, makeVoidType(), $3.getExpr(), $5.getExpr(), $6.getExpr(), NULL), $1);
                         }
-    | STORED '(' expression optFewMany ')'
+    | STORED '(' expression ',' fewMany optStoredFieldFormat ')'
+                        {
+                            parser->normalizeStoredNameExpression($3);
+                            $$.setExpr(createValue(no_stored, makeVoidType(), $3.getExpr(), $5.getExpr(), $6.getExpr()), $1);
+                        }
+    | STORED '(' expression optStoredFieldFormat ')'
                         {
                             parser->normalizeStoredNameExpression($3);
                             $$.setExpr(createValue(no_stored, makeVoidType(), $3.getExpr(), $4.getExpr()), $1);
@@ -1715,6 +1728,18 @@ persistOpt
                         }
     ;
 
+optStoredFieldFormat
+    :                   {
+                            $$.setNullExpr();
+                        }
+    | ',' FORMAT '(' hintList ')'
+                        {
+                            HqlExprArray args;
+                            $4.unwindCommaList(args);
+                            $$.setExpr(createExprAttribute(storedFieldFormatAtom, args), $2);
+                        }
+    ;
+
 globalOpts
     :                   {   $$.setNullExpr(); }
     | ',' globalOpts2   {   $$.inherit($2); }

+ 2 - 0
ecl/hql/hqlgram2.cpp

@@ -10452,6 +10452,7 @@ static void getTokenText(StringBuffer & msg, int token)
     case FIRST: msg.append("FIRST"); break;
     case TOK_FIXED: msg.append("FIXED"); break;
     case FLAT: msg.append("FLAT"); break;
+    case FORMAT: msg.append("FORMAT"); break;
     case FORMAT_ATTR: msg.append("FORMAT"); break;
     case FORWARD: msg.append("FORWARD"); break;
     case FROM: msg.append("FROM"); break;
@@ -10717,6 +10718,7 @@ static void getTokenText(StringBuffer & msg, int token)
     case HASH_STORED: msg.append("#STORED"); break;
     case HASH_LINK: msg.append("#LINK"); break;
     case HASH_WORKUNIT: msg.append("#WORKUNIT"); break;
+    case HASH_WEBSERVICE: msg.append("#WEBSERVICE"); break;
     case SIMPLE_TYPE: msg.append("type-name"); break;
 
     case EQ: msg.append("="); break;

+ 7 - 0
ecl/hql/hqllex.l

@@ -565,6 +565,12 @@ xpathseq      ([^}\r\n])+
                             return SKIPPED; 
                         return(HASH_WORKUNIT); 
                     }
+#WEBSERVICE         {
+                        setupdatepos;
+                        if (lexer->skipNesting || lexer->macroGathering)
+                            return SKIPPED;
+                        return(HASH_WEBSERVICE);
+                    }
 __LINE__            {
                         setupdatepos; 
                         returnToken.setExpr(createIntegerConstant(lexer->yyLineNo, false));
@@ -699,6 +705,7 @@ FILTERED            { RETURNSYM(FILTERED); }
 FIRST               { RETURNSYM(FIRST); }
 FIXED               { RETURNSYM(TOK_FIXED); }
 FLAT                { RETURNSYM(FLAT); }
+FORMAT              { RETURNSYM(FORMAT); }
 FROM                { RETURNSYM(FROM); }
 FORMAT              { RETURNSYM(FORMAT_ATTR); }
 FORWARD             { RETURNSYM(FORWARD); }

+ 2 - 0
ecl/hql/hqlthql.cpp

@@ -2368,6 +2368,8 @@ void HqltHql::toECL(IHqlExpression *expr, StringBuffer &s, bool paren, bool inTy
                     s.append("#STORED");
                 else if (kind == workunitAtom)
                     s.append("#WORKUNIT");
+                else if (kind == webserviceAtom)
+                    s.append("#WEBSERVICE");
                 else
                     s.append("#META:").append(kind);
 

+ 58 - 0
ecl/hql/hqlutil.cpp

@@ -2955,6 +2955,64 @@ bool isValidXmlRecord(IHqlExpression * expr)
     return true;
 }
 
+static void expandHintValue(StringBuffer & s, IHqlExpression * expr)
+{
+    node_operator op = expr->getOperator();
+    node_operator childOp = no_none;
+    switch (op)
+    {
+    case no_constant:
+        expr->queryValue()->getStringValue(s);
+        break;
+    case no_comma:
+        expandHintValue(s, expr->queryChild(0));
+        expandHintValue(s.append(","), expr->queryChild(1));
+        break;
+    case no_range:
+        expandHintValue(s, expr->queryChild(0));
+        expandHintValue(s.append(".."), expr->queryChild(1));
+        break;
+    case no_rangefrom:
+        expandHintValue(s, expr->queryChild(0));
+        s.append("..");
+        break;
+    case no_rangeto:
+        expandHintValue(s.append(".."), expr->queryChild(0));
+        break;
+    case no_list:
+        {
+            s.append("[");
+            ForEachChild(i, expr)
+            {
+                if (i)
+                    s.append(",");
+                expandHintValue(s, expr->queryChild(i));
+            }
+            s.append("]");
+            break;
+        }
+    case no_attr:
+        s.append(expr->queryName());
+        break;
+    default:
+        s.append("?");
+        break;
+    }
+}
+
+void getHintNameValue(IHqlExpression * attr, StringBuffer &name, StringBuffer &value)
+{
+    name.set(attr->queryName()->str());
+    ForEachChild(i, attr)
+    {
+        if (i)
+            value.append(",");
+        expandHintValue(value, attr->queryChild(i));
+    }
+    if (value.length() == 0)
+        value.append("1");
+}
+
 bool getBoolValue(IHqlExpression * expr, bool dft)
 {
     if (expr)

+ 1 - 0
ecl/hql/hqlutil.hpp

@@ -111,6 +111,7 @@ extern HQL_API bool isValidXmlRecord(IHqlExpression * expr);
 extern HQL_API bool matchesConstantValue(IHqlExpression * expr, __int64 test);
 extern HQL_API bool matchesBoolean(IHqlExpression * expr, bool test);
 extern HQL_API bool matchesConstantString(IHqlExpression * expr, const char * text, bool ignoreCase);
+extern HQL_API void getHintNameValue(IHqlExpression * attr, StringBuffer &name, StringBuffer &value);
 extern HQL_API bool getBoolValue(IHqlExpression * expr, bool dft);
 extern HQL_API __int64 getIntValue(IHqlExpression * expr, __int64 dft = 0);
 extern HQL_API StringBuffer & getStringValue(StringBuffer & out, IHqlExpression * expr, const char * dft = NULL);

+ 20 - 57
ecl/hqlcpp/hqlhtcpp.cpp

@@ -1880,49 +1880,6 @@ void ActivityInstance::removeAttribute(const char * name)
     removeGraphAttribute(graphNode, name);
 }
 
-static void expandHintValue(StringBuffer & s, IHqlExpression * expr)
-{
-    switch (expr->getOperator())
-    {
-    case no_constant:
-        expr->queryValue()->getStringValue(s);
-        break;
-    case no_comma:
-        expandHintValue(s, expr->queryChild(0));
-        expandHintValue(s.append(","), expr->queryChild(1));
-        break;
-    case no_range:
-        expandHintValue(s, expr->queryChild(0));
-        expandHintValue(s.append(".."), expr->queryChild(1));
-        break;
-    case no_rangefrom:
-        expandHintValue(s, expr->queryChild(0));
-        s.append("..");
-        break;
-    case no_rangeto:
-        expandHintValue(s.append(".."), expr->queryChild(0));
-        break;
-    case no_list:
-        {
-            s.append("[");
-            ForEachChild(i, expr)
-            {
-                if (i)
-                    s.append(",");
-                expandHintValue(s, expr->queryChild(i));
-            }
-            s.append("]");
-            break;
-        }
-    case no_attr:
-        s.append(expr->queryName());
-        break;
-    default:
-        s.append("?");
-        break;
-    }
-}
-
 void ActivityInstance::processAnnotation(IHqlExpression * annotation)
 {
     switch (annotation->getAnnotationKind())
@@ -1966,20 +1923,12 @@ void ActivityInstance::processAnnotations(IHqlExpression * expr)
 
 void ActivityInstance::processHint(IHqlExpression * attr)
 {
-    IAtom * name = attr->queryName();
+    StringBuffer name;
     StringBuffer value;
-    ForEachChild(i, attr)
-    {
-        if (i)
-            value.append(",");
-        expandHintValue(value, attr->queryChild(i));
-    }
-    if (value.length() == 0)
-        value.append("1");
-
+    getHintNameValue(attr, name, value);
     IPropertyTree * att = createPTree();
-    att->setProp("@name", name->str());
-    att->setProp("@value", value.str());
+    att->setProp("@name", name);
+    att->setProp("@value", value);
     graphNode->addPropTree("hint", att);
 }
 
@@ -5299,12 +5248,13 @@ void HqlCppTranslator::buildSetResultInfo(BuildCtx & ctx, IHqlExpression * origi
     {
         HqlExprArray xmlnsAttrs;
         gatherAttributes(xmlnsAttrs, xmlnsAtom, originalExpr);
+        Owned<IWUResult> result;
         if (retType == type_row)
         {
             OwnedHqlExpr record = LINK(::queryRecord(schemaType));
             if (originalExpr->hasAttribute(noXpathAtom))
                 record.setown(removeAttributeFromFields(record, xpathAtom));
-            Owned<IWUResult> result = createDatasetResultSchema(seq, name, record, xmlnsAttrs, false, false);
+            result.setown(createDatasetResultSchema(seq, name, record, xmlnsAttrs, false, false));
             if (result)
                 result->setResultTotalRowCount(1);
         }
@@ -5312,7 +5262,7 @@ void HqlCppTranslator::buildSetResultInfo(BuildCtx & ctx, IHqlExpression * origi
         {
             // Bit of a mess - should split into two procedures
             int sequence = (int) seq->queryValue()->getIntValue();
-            Owned<IWUResult> result = createWorkunitResult(sequence, name);
+            result.setown(createWorkunitResult(sequence, name));
             if(result)
             {
                 StringBuffer fieldName;
@@ -5346,6 +5296,19 @@ void HqlCppTranslator::buildSetResultInfo(BuildCtx & ctx, IHqlExpression * origi
                 addSchemaResource(sequence, resultName.str(), xml.length()+1, xml.str());
             }
         }
+
+        IHqlExpression * format =  originalExpr->queryAttribute(storedFieldFormatAtom);
+        if (format)
+        {
+            ForEachChild(i, format)
+            {
+                StringBuffer name;
+                StringBuffer value;
+                OwnedHqlExpr folded = foldHqlExpression(format->queryChild(i));
+                getHintNameValue(folded, name, value);
+                result->setResultFieldOpt(name, value);
+            }
+        }
     }
 }
 

+ 17 - 1
ecl/hqlcpp/hqlttcpp.cpp

@@ -164,6 +164,7 @@ public:
     OwnedHqlExpr storedName;
     OwnedHqlExpr originalLabel;
     OwnedHqlExpr sequence;
+    OwnedHqlExpr fieldFormat;
     node_operator setOp;
     node_operator persistOp;
 protected:
@@ -281,7 +282,19 @@ void NewThorStoredReplacer::doAnalyseBody(IHqlExpression * expr)
         StringBuffer errorTemp;
         seenMeta = true;
         IAtom * kind = expr->queryChild(0)->queryName();
-        if (kind == debugAtom)
+        if (kind == webserviceAtom)
+        {
+            Owned<IWUWebServicesInfo> wsi = wu->updateWebServicesInfo(true);
+            IHqlExpression *wsExpr = expr->queryChild(0);
+            ForEachChild(i, wsExpr)
+            {
+                StringBuffer name, value;
+                OwnedHqlExpr folded = foldHqlExpression(wsExpr->queryChild(i));
+                getHintNameValue(folded, name, value);
+                wsi->setText(name, value);
+            }
+        }
+        else if (kind == debugAtom)
         {
             OwnedHqlExpr foldedName = foldHqlExpression(expr->queryChild(1));
             OwnedHqlExpr foldedValue = foldHqlExpression(expr->queryChild(2));
@@ -4969,6 +4982,8 @@ IHqlExpression * GlobalAttributeInfo::createSetValue(IHqlExpression * value, IHq
         extraSetAttr->unwindList(args, no_comma);
     if (cluster)
         args.append(*createAttribute(clusterAtom, LINK(cluster)));
+    if (fieldFormat)
+        args.append(*LINK(fieldFormat));
     if (setOp == no_setresult)
         return createSetResult(args);
     return createValue(setOp, makeVoidType(), args);
@@ -5011,6 +5026,7 @@ void GlobalAttributeInfo::extractStoredInfo(IHqlExpression * expr, IHqlExpressio
         storedName.set(expr->queryChild(0));
         originalLabel.set(storedName);
         sequence.setown(getStoredSequenceNumber());
+        fieldFormat.set(expr->queryAttribute(storedFieldFormatAtom));
         few = true;
         break;
     case no_checkpoint:

+ 86 - 65
esp/services/ws_ecl/ws_ecl_service.cpp

@@ -777,7 +777,7 @@ void buildSampleDataset(StringBuffer &xml, IPropertyTree *xsdtree, const char *s
 void CWsEclBinding::buildSampleResponseXml(StringBuffer& msg, IEspContext &context, CHttpRequest* request, WsEclWuInfo &wsinfo)
 {
     StringBuffer element;
-    element.append(wsinfo.queryname.sget()).append("Response");
+    element.append(wsinfo.queryname.str()).append("Response");
 
     StringBuffer xsds;
     wsinfo.getSchemas(xsds);
@@ -787,7 +787,7 @@ void CWsEclBinding::buildSampleResponseXml(StringBuffer& msg, IEspContext &conte
         msg.append("<?xml-stylesheet type=\"text/xsl\" href=\"/esp/xslt/xmlformatter.xsl\"?>");
 
     msg.append('<').append(element.str()).append(" xmlns=\"urn:hpccsystems:ecl:");
-    msg.appendLower(wsinfo.queryname.length(), wsinfo.queryname.sget()).append("\">");
+    msg.appendLower(wsinfo.queryname.length(), wsinfo.queryname.str()).append("\">");
     msg.append("<Results><Result>");
 
     Owned<IPropertyTree> xsds_tree;
@@ -798,7 +798,7 @@ void CWsEclBinding::buildSampleResponseXml(StringBuffer& msg, IEspContext &conte
     {
         Owned<IPropertyTreeIterator> result_xsds =xsds_tree->getElements("Result");
         ForEach (*result_xsds)
-            buildSampleDataset(msg, result_xsds->query().queryPropTree("xs:schema"), wsinfo.qsetname.sget(), wsinfo.queryname.sget(), result_xsds->query().queryProp("@name"));
+            buildSampleDataset(msg, result_xsds->query().queryPropTree("xs:schema"), wsinfo.qsetname.str(), wsinfo.queryname.str(), result_xsds->query().queryProp("@name"));
     }
 
     msg.append("</Result></Results>");
@@ -811,8 +811,8 @@ int CWsEclBinding::getWsEclLinks(IEspContext &context, CHttpRequest* request, CH
     StringBuffer xml;
     xml.append("<links>");
     xml.append("<version>3</version>");
-    xml.append("<path>").append(wsinfo.qsetname.sget()).append("</path>");
-    xml.append("<query>").append(wsinfo.queryname.sget()).append("</query>");
+    xml.append("<path>").append(wsinfo.qsetname.str()).append("</path>");
+    xml.append("<query>").append(wsinfo.queryname.str()).append("</query>");
 
     StringBuffer xsds;
     wsinfo.getSchemas(xsds);
@@ -879,8 +879,8 @@ int CWsEclBinding::getWsEcl2TabView(CHttpRequest* request, CHttpResponse* respon
     xml.append("<tabview>");
     xml.append("<version>3</version>");
     xml.appendf("<wuid>%s</wuid>", w);
-    xml.appendf("<qset>%s</qset>", wsinfo.qsetname.sget());
-    xml.appendf("<qname>%s</qname>", wsinfo.queryname.sget());
+    xml.appendf("<qset>%s</qset>", wsinfo.qsetname.str());
+    xml.appendf("<qname>%s</qname>", wsinfo.queryname.str());
 
     StringBuffer xsds;
     wsinfo.getSchemas(xsds);
@@ -1119,14 +1119,12 @@ void appendEclInputXsds(StringBuffer &content, IPropertyTree *xsd, BoolHash &add
     }
 }
 
-void CWsEclBinding::SOAPSectionToXsd(WsEclWuInfo &wsinfo, const char *parmXml, StringBuffer &schema, bool isRequest, IPropertyTree *xsdtree)
+void CWsEclBinding::SOAPSectionToXsd(WsEclWuInfo &wuinfo, IPropertyTree *parmTree, StringBuffer &schema, bool isRequest, IPropertyTree *xsdtree)
 {
-    Owned<IPropertyTree> tree = createPTreeFromXMLString(parmXml);
-
-    schema.appendf("<xsd:element name=\"%s%s\">", wsinfo.queryname.sget(), isRequest ? "Request" : "Response");
+    schema.appendf("<xsd:element name=\"%s%s\">", wuinfo.queryname.str(), isRequest ? "Request" : "Response");
     schema.append("<xsd:complexType>");
     schema.append("<xsd:all>");
-    Owned<IPropertyTreeIterator> parts = tree->getElements("part");
+    Owned<IPropertyTreeIterator> parts = parmTree->getElements("part");
     if (parts)
     {
         ForEach(*parts)
@@ -1151,13 +1149,16 @@ void CWsEclBinding::SOAPSectionToXsd(WsEclWuInfo &wsinfo, const char *parmXml, S
             }
 
             schema.appendf("<xsd:element minOccurs=\"0\" maxOccurs=\"1\" name=\"%s\" type=\"%s\"", name, type.str());
-            if (strieq(type.str(), "tns:XmlDataSet"))
+            if (part.hasProp("@width") || part.hasProp("@height"))
             {
-                schema.append(">"
-                        "<xsd:annotation><xsd:appinfo>"
-                            "<form formRows=\"25\" formCols=\"60\"/>"
-                        "</xsd:appinfo></xsd:annotation>"
-                    "</xsd:element>");
+                schema.append("><xsd:annotation><xsd:appinfo><form");
+                unsigned rows = part.getPropInt("@height");
+                if (rows)
+                    schema.appendf(" formRows='%u'", rows);
+                unsigned cols = part.getPropInt("@width");
+                if (cols)
+                    schema.appendf(" formCols='%u'", cols);
+                schema.appendf("/></xsd:appinfo></xsd:annotation></xsd:element>");
             }
             else
                 schema.append("/>");
@@ -1213,22 +1214,23 @@ int CWsEclBinding::getXsdDefinition(IEspContext &context, CHttpRequest *request,
 
         if (wsinfo.queryname.length()>0)
         {
-            StringBuffer parmXml;
-            if (wsinfo.getWsResource("SOAP", parmXml))
+
+            IPropertyTree *parmTree = wsinfo.queryParamInfo();
+            if (xsdtree)
             {
-                if (xsdtree)
+                BoolHash added;
+                Owned<IPropertyTreeIterator> input_xsds =xsdtree->getElements("Input");
+                ForEach (*input_xsds)
                 {
-                    BoolHash added;
-                    Owned<IPropertyTreeIterator> input_xsds =xsdtree->getElements("Input");
-                    ForEach (*input_xsds)
-                    {
-                        appendEclInputXsds(content, &input_xsds->query(), added);
-                    }
+                    IPropertyTree &input = input_xsds->query();
+                    VStringBuffer xpath("part[@name='%s']", input.queryProp("@name"));
+                    if (parmTree->hasProp(xpath))
+                        appendEclInputXsds(content, &input, added);
                 }
-                SOAPSectionToXsd(wsinfo, parmXml.str(), content, true, xsdtree);
             }
+            SOAPSectionToXsd(wsinfo, parmTree, content, true, xsdtree);
 
-            content.appendf("<xsd:element name=\"%sResponse\">", wsinfo.queryname.sget());
+            content.appendf("<xsd:element name=\"%sResponse\">", wsinfo.queryname.str());
             content.append("<xsd:complexType>");
             content.append("<xsd:all>");
             content.append("<xsd:element name=\"Exceptions\" type=\"tns:ArrayOfEspException\" minOccurs=\"0\"/>");
@@ -1280,7 +1282,7 @@ bool CWsEclBinding::getSchema(StringBuffer& schema, IEspContext &ctx, CHttpReque
     Owned<IPropertyTreeIterator> nsiter = namespaces->getElements("namespace");
     
     StringBuffer urn("urn:hpccsystems:ecl:");
-    urn.appendLower(wsinfo.queryname.length(), wsinfo.queryname.sget());
+    urn.appendLower(wsinfo.queryname.length(), wsinfo.queryname.str());
     schema.appendf("<xsd:schema elementFormDefault=\"qualified\" targetNamespace=\"%s\" ", urn.str());
     schema.appendf(" xmlns:tns=\"%s\"  xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"", urn.str());
     ForEach(*nsiter)
@@ -1323,9 +1325,9 @@ bool CWsEclBinding::getSchema(StringBuffer& schema, IEspContext &ctx, CHttpReque
     return true;
 }
 
-int CWsEclBinding::getGenForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo, bool box)
+int CWsEclBinding::getGenForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wuinfo, bool box)
 {
-    IConstWorkUnit *wu = wsinfo.ensureWorkUnit();
+    IConstWorkUnit *wu = wuinfo.ensureWorkUnit();
     IProperties *parms = request->queryParameters();
 
     StringBuffer page;
@@ -1333,24 +1335,43 @@ int CWsEclBinding::getGenForm(IEspContext &context, CHttpRequest* request, CHttp
 
     StringBuffer v;
     StringBuffer formxml("<FormInfo>");
-    appendXMLTag(formxml, "WUID", wsinfo.queryWuid());
-    appendXMLTag(formxml, "QuerySet", wsinfo.qsetname.sget());
-    appendXMLTag(formxml, "QueryName", wsinfo.queryname.sget());
+    appendXMLTag(formxml, "WUID", wuinfo.queryWuid());
+    appendXMLTag(formxml, "QuerySet", wuinfo.qsetname.str());
+    appendXMLTag(formxml, "QueryName", wuinfo.queryname.str());
     appendXMLTag(formxml, "ClientVersion", v.appendf("%g",context.getClientVersion()).str());
-    appendXMLTag(formxml, "RequestElement", v.clear().append(wsinfo.queryname).append("Request").str());
+    appendXMLTag(formxml, "RequestElement", v.clear().append(wuinfo.queryname).append("Request").str());
+
+    StringBuffer help;
+    StringBuffer info;
+
+    Owned<IConstWUWebServicesInfo> ws = wu->getWebServicesInfo();
+    if (ws)
+    {
+        StringBufferAdaptor helpSv(help);
+        StringBufferAdaptor infoSv(info);
+
+        ws->getText("help", helpSv);
+        ws->getText("description", infoSv);
+    }
 
-    Owned<IWuWebView> web = createWuWebView(*wu, wsinfo.qsetname.get(), wsinfo.queryname.get(), getCFD(), true);
+    Owned<IWuWebView> web = createWuWebView(*wu, wuinfo.qsetname.get(), wuinfo.queryname.get(), getCFD(), true);
     if (web)
     {
-        appendXMLTag(formxml, "Help", web->aggregateResources("HELP", v.clear()).str());
-        appendXMLTag(formxml, "Info", web->aggregateResources("INFO", v.clear()).str());
+        if (!help.length())
+            web->aggregateResources("HELP", help);
+        if (!info.length())
+            web->aggregateResources("INFO", info);;
     }
+    if (help.length())
+        appendXMLTag(formxml, "Help", help.str());
+    if (info.length())
+        appendXMLTag(formxml, "Info", info.str());
 
     context.addOptions(ESPCTX_ALL_ANNOTATION);
     if (box)
     {
         StringBuffer xmlreq;
-        getWsEcl2XmlRequest(xmlreq, context, request, wsinfo, "xml", NULL, 0, true);
+        getWsEcl2XmlRequest(xmlreq, context, request, wuinfo, "xml", NULL, 0, true);
         if (xmlreq.length())
         {
             Owned<IPropertyTree> pretty = createPTreeFromXMLString(xmlreq.str(), ipt_ordered);
@@ -1364,7 +1385,7 @@ int CWsEclBinding::getGenForm(IEspContext &context, CHttpRequest* request, CHttp
         }
     }
     else
-        getSchema(formxml, context, request, wsinfo);
+        getSchema(formxml, context, request, wuinfo);
 
     formxml.append("<CustomViews>");
     if (web)
@@ -1456,7 +1477,7 @@ void CWsEclBinding::getWsEcl2XmlRequest(StringBuffer& soapmsg, IEspContext &cont
     else
     {
         StringBuffer element;
-        element.append(wsinfo.queryname.sget()).append("Request");
+        element.append(wsinfo.queryname.str()).append("Request");
 
         StringBuffer schemaXml;
         getSchema(schemaXml, context, request, wsinfo);
@@ -1469,7 +1490,7 @@ void CWsEclBinding::getWsEcl2XmlRequest(StringBuffer& soapmsg, IEspContext &cont
             if (type)
             {
                 StringArray parentTypes;
-                buildReqXml(parentTypes, type, soapmsg, (!stricmp(xmltype, "roxiexml")) ? wsinfo.queryname.sget() : element.str(), reqTree, flags|REQSF_ROOT, ns);
+                buildReqXml(parentTypes, type, soapmsg, (!stricmp(xmltype, "roxiexml")) ? wsinfo.queryname.str() : element.str(), reqTree, flags|REQSF_ROOT, ns);
             }
         }
     }
@@ -1492,7 +1513,7 @@ void CWsEclBinding::getWsEclJsonRequest(StringBuffer& jsonmsg, IEspContext &cont
             return;
         }
         StringBuffer element;
-        element.append(wsinfo.queryname.sget());
+        element.append(wsinfo.queryname.str());
             element.append("Request");
 
         StringBuffer schemaXml;
@@ -1506,7 +1527,7 @@ void CWsEclBinding::getWsEclJsonRequest(StringBuffer& jsonmsg, IEspContext &cont
             if (type)
             {
                 StringArray parentTypes;
-                JsonHelpers::buildJsonMsg(parentTypes, type, jsonmsg, wsinfo.queryname.sget(), reqTree, flags|REQSF_ROOT);
+                JsonHelpers::buildJsonMsg(parentTypes, type, jsonmsg, wsinfo.queryname.str(), reqTree, flags|REQSF_ROOT);
             }
         }
     }
@@ -1528,7 +1549,7 @@ void CWsEclBinding::getSoapMessage(StringBuffer& soapmsg, IEspContext &context,
         );
 
     StringBuffer ns;
-    ns.append("xmlns=\"urn:hpccsystems:ecl:").appendLower(wsinfo.queryname.length(), wsinfo.queryname.sget()).append('\"');
+    ns.append("xmlns=\"urn:hpccsystems:ecl:").appendLower(wsinfo.queryname.length(), wsinfo.queryname.str()).append('\"');
     getWsEcl2XmlRequest(soapmsg, context, request, wsinfo, "soap", ns.str(), flags, validate);
 
     soapmsg.append("</soap:Body></soap:Envelope>");
@@ -1595,8 +1616,8 @@ int CWsEclBinding::getXmlTestForm(IEspContext &context, CHttpRequest* request, C
 
     // params
     xform->setStringParameter("pageName", pageName.str());
-    xform->setStringParameter("serviceName", wsinfo.qsetname.sget());
-    xform->setStringParameter("methodName", wsinfo.queryname.sget());
+    xform->setStringParameter("serviceName", wsinfo.qsetname.str());
+    xform->setStringParameter("methodName", wsinfo.queryname.str());
     xform->setStringParameter("wuid", wsinfo.queryWuid());
     xform->setStringParameter("header", header.str());
 
@@ -1647,8 +1668,8 @@ int CWsEclBinding::getJsonTestForm(IEspContext &context, CHttpRequest* request,
 
     // params
     xform->setStringParameter("pageName", pageName.str());
-    xform->setStringParameter("serviceName", wsinfo.qsetname.sget());
-    xform->setStringParameter("methodName", wsinfo.queryname.sget());
+    xform->setStringParameter("serviceName", wsinfo.qsetname.str());
+    xform->setStringParameter("methodName", wsinfo.queryname.str());
     xform->setStringParameter("wuid", wsinfo.queryWuid());
     xform->setStringParameter("header", header.str());
 
@@ -1744,7 +1765,7 @@ int CWsEclBinding::submitWsEclWorkunit(IEspContext & context, WsEclWuInfo &wsinf
 
     workunit->clearExceptions();
     workunit->resetWorkflow();
-    workunit->setClusterName(wsinfo.qsetname.sget());
+    workunit->setClusterName(wsinfo.qsetname.str());
     workunit->setUser(context.queryUserId());
 
     const char *jobname = context.queryRequestParameters()->queryProp("_jobname");
@@ -1772,7 +1793,7 @@ int CWsEclBinding::submitWsEclWorkunit(IEspContext & context, WsEclWuInfo &wsinf
     workunit->schedule();
     workunit.clear();
 
-    runWorkUnit(wuid.str(), wsinfo.qsetname.sget());
+    runWorkUnit(wuid.str(), wsinfo.qsetname.str());
 
     bool async = context.queryRequestParameters()->hasProp("_async");
 
@@ -1971,12 +1992,12 @@ int CWsEclBinding::getWsdlMessages(IEspContext &context, CHttpRequest *request,
     WsEclWuInfo *wsinfo = (WsEclWuInfo *) context.getBindingValue();
     if (wsinfo)
     {
-        content.appendf("<message name=\"%sSoapIn\">", wsinfo->queryname.sget());
-        content.appendf("<part name=\"parameters\" element=\"tns:%sRequest\"/>", wsinfo->queryname.sget());
+        content.appendf("<message name=\"%sSoapIn\">", wsinfo->queryname.str());
+        content.appendf("<part name=\"parameters\" element=\"tns:%sRequest\"/>", wsinfo->queryname.str());
         content.append("</message>");
 
-        content.appendf("<message name=\"%sSoapOut\">", wsinfo->queryname.sget());
-        content.appendf("<part name=\"parameters\" element=\"tns:%sResponse\"/>", wsinfo->queryname.sget());
+        content.appendf("<message name=\"%sSoapOut\">", wsinfo->queryname.str());
+        content.appendf("<part name=\"parameters\" element=\"tns:%sResponse\"/>", wsinfo->queryname.str());
         content.append("</message>");
     }
 
@@ -1988,10 +2009,10 @@ int CWsEclBinding::getWsdlPorts(IEspContext &context, CHttpRequest *request, Str
     WsEclWuInfo *wsinfo = (WsEclWuInfo *) context.getBindingValue();
     if (wsinfo)
     {
-        content.appendf("<portType name=\"%sServiceSoap\">", wsinfo->qsetname.sget());
-        content.appendf("<operation name=\"%s\">", wsinfo->queryname.sget());
-        content.appendf("<input message=\"tns:%sSoapIn\"/>", wsinfo->queryname.sget());
-        content.appendf("<output message=\"tns:%sSoapOut\"/>", wsinfo->queryname.sget());
+        content.appendf("<portType name=\"%sServiceSoap\">", wsinfo->qsetname.str());
+        content.appendf("<operation name=\"%s\">", wsinfo->queryname.str());
+        content.appendf("<input message=\"tns:%sSoapIn\"/>", wsinfo->queryname.str());
+        content.appendf("<output message=\"tns:%sSoapOut\"/>", wsinfo->queryname.str());
         content.append("</operation>");
         content.append("</portType>");
     }
@@ -2003,11 +2024,11 @@ int CWsEclBinding::getWsdlBindings(IEspContext &context, CHttpRequest *request,
     WsEclWuInfo *wsinfo = (WsEclWuInfo *) context.getBindingValue();
     if (wsinfo)
     {
-        content.appendf("<binding name=\"%sServiceSoap\" type=\"tns:%sServiceSoap\">", wsinfo->qsetname.sget(), wsinfo->qsetname.sget());
+        content.appendf("<binding name=\"%sServiceSoap\" type=\"tns:%sServiceSoap\">", wsinfo->qsetname.str(), wsinfo->qsetname.str());
         content.append("<soap:binding transport=\"http://schemas.xmlsoap.org/soap/http\" style=\"document\"/>");
 
-        content.appendf("<operation name=\"%s\">", wsinfo->queryname.sget());
-        content.appendf("<soap:operation soapAction=\"/%s/%s?ver_=1.0\" style=\"document\"/>", wsinfo->qsetname.sget(), wsinfo->queryname.sget());
+        content.appendf("<operation name=\"%s\">", wsinfo->queryname.str());
+        content.appendf("<soap:operation soapAction=\"/%s/%s?ver_=1.0\" style=\"document\"/>", wsinfo->qsetname.str(), wsinfo->queryname.str());
         content.append("<input>");
         content.append("<soap:body use=\"literal\"/>");
         content.append("</input>");
@@ -2023,7 +2044,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.sget(), wsinfo.queryname.sget());
+    EspHttpBinding::onGetWsdl(context, request, response, wsinfo.qsetname.str(), wsinfo.queryname.str());
     context.setBindingValue(NULL);
     return 0;
 }
@@ -2031,7 +2052,7 @@ 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.sget(), wsinfo.queryname.sget());
+    EspHttpBinding::onGetXsd(context, request, response, wsinfo.qsetname.str(), wsinfo.queryname.str());
     context.setBindingValue(NULL);
 
     return 0;

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

@@ -161,7 +161,7 @@ public:
     void appendSchemaNamespaces(IPropertyTree *namespaces, IEspContext &ctx, CHttpRequest* req, WsEclWuInfo &wsinfo);
     void appendSchemaNamespaces(IPropertyTree *namespaces, IEspContext &ctx, CHttpRequest* req, const char *service, const char *method);
 
-    void SOAPSectionToXsd(WsEclWuInfo &wsinfo, const char *parmXml, StringBuffer &schema, bool isRequest=true, IPropertyTree *xsdtree=NULL);
+    void SOAPSectionToXsd(WsEclWuInfo &wsinfo, IPropertyTree *parmTree, StringBuffer &schema, bool isRequest=true, IPropertyTree *xsdtree=NULL);
     int getXmlTestForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, const char *formtype, WsEclWuInfo &wsinfo);
     int getXmlTestForm(IEspContext &context, CHttpRequest* request, CHttpResponse* response, WsEclWuInfo &wsinfo, const char *formtype);
 

+ 128 - 63
esp/services/ws_ecl/ws_ecl_wuinfo.cpp

@@ -37,82 +37,147 @@ IConstWorkUnit *WsEclWuInfo::ensureWorkUnit()
     if (wu)
         return wu;
     Owned<IWorkUnitFactory> wf = getWorkUnitFactory();
-    wu.setown(wf->openWorkUnit(wuid.sget(), false));
+    wu.setown(wf->openWorkUnit(wuid.str(), false));
     if (!wu)
-        throw MakeStringException(-1, "Could not open workunit: %s", wuid.sget());
+        throw MakeStringException(-1, "Could not open workunit: %s", wuid.str());
     if (isLibrary(wu))
-        throw MakeStringException(-1, "%s/%s %s is a library", qsetname.sget(), queryname.sget(), wuid.sget());
+        throw MakeStringException(-1, "%s/%s %s is a library", qsetname.str(), queryname.str(), wuid.str());
     return wu;
 }
 
-bool WsEclWuInfo::getWsResource(const char *name, StringBuffer &out)
+void appendVariableParmInfo(IArrayOf<IPropertyTree> &parts, IResultSetFactory *resultSetFactory, IConstWUResult &var, unsigned hashWebserviceSeq=0)
 {
-    if (strieq(name, "SOAP"))
-    {
-        out.appendf("<message name=\"%s\">", queryname.sget());
-        IConstWUResultIterator &vars = ensureWorkUnit()->getVariables();
-        Owned<IResultSetFactory> resultSetFactory(getResultSetFactory(username, password));
-        ForEach(vars)
-        {
-            IConstWUResult &var = vars.query();
-            SCMStringBuffer varname;
-            var.getResultName(varname);
-            int seq = var.getResultSequence();
+    SCMStringBuffer varname;
+    var.getResultName(varname);
+    int seq = var.getResultSequence();
 
-            WUResultFormat fmt = var.getResultFormat();
+    WUResultFormat fmt = var.getResultFormat();
 
-            SCMStringBuffer eclschema;
-            var.getResultEclSchema(eclschema);
+    SCMStringBuffer eclschema;
+    var.getResultEclSchema(eclschema);
 
-            SCMStringBuffer s;
-            Owned<IResultSetMetaData> meta = resultSetFactory->createResultSetMeta(&var);
+    SCMStringBuffer s;
+    Owned<IResultSetMetaData> meta = resultSetFactory->createResultSetMeta(&var);
+    StringBuffer width, height, fieldSeq;
+    var.getResultFieldOpt("fieldwidth", StringBufferAdaptor(width));
+    var.getResultFieldOpt("fieldheight", StringBufferAdaptor(height));
+    if (hashWebserviceSeq)
+        fieldSeq.append(hashWebserviceSeq);
+    else
+        var.getResultFieldOpt("sequence", StringBufferAdaptor(fieldSeq));
 
-            if (!var.isResultScalar())
-            {
-                meta->getXmlSchema(s, false);
-                out.appendf("<part name=\"%s\" type=\"tns:XmlDataSet\" />", varname.str());
-            }
-            else
+    Owned<IPropertyTree> part = createPTree("part");
+    if (!var.isResultScalar())
+    {
+        meta->getXmlSchema(s, false);
+        part->setProp("@name", varname.str());
+        part->setProp("@type", "tns:XmlDataset");
+        if (fieldSeq.length())
+            part->setProp("@sequence", fieldSeq);
+    }
+    else
+    {
+        meta->getColumnEclType(s, 0);
+        DisplayType dt = meta->getColumnDisplayType(0);
+        StringAttr ptype;
+        switch (dt)
+        {
+        case TypeBoolean:
+            ptype.set("xsd:boolean");
+            break;
+        case TypeInteger:
+            ptype.set("xsd:integer");
+            break;
+        case TypeUnsignedInteger:
+            ptype.set("xsd:integer");
+            break;
+        case TypeReal:
+            ptype.set("xsd:real");
+            break;
+        case TypeSet:
+            ptype.set("tns:EspStringArray");
+            break;
+        case TypeDataset:
+        case TypeData:
+            ptype.set("tns:XmlDataSet");
+            break;
+        case TypeUnicode:
+        case TypeString:
+            ptype.set("xsd:string");
+            break;
+        case TypeUnknown:
+        case TypeBeginIfBlock:
+        case TypeEndIfBlock:
+        case TypeBeginRecord:
+        default:
+            ptype.set("xsd:string");
+            break;
+        }
+        part->setProp("@name", varname.str());
+        part->setProp("@type", ptype.str());
+        if (width.length())
+            part->setProp("@width", width);
+        if (height.length())
+            part->setProp("@height", height);
+        if (fieldSeq.length())
+            part->setProp("@sequence", fieldSeq);
+    }
+    parts.append(*part.getClear());
+}
+
+int orderParts(IInterface * const * pLeft, IInterface * const * pRight)
+{
+    IPropertyTree * right = (IPropertyTree *)*pRight;
+    IPropertyTree * left = (IPropertyTree *)*pLeft;
+    bool hasRightSeq = right->hasProp("@sequence");
+    bool hasLeftSeq = left->hasProp("@sequence");
+    if (hasRightSeq && hasLeftSeq)
+        return left->getPropInt("@sequence") - right->getPropInt("@sequence");
+    if (hasRightSeq);
+        return -1;
+    if (hasLeftSeq)
+        return 1;
+    if (!right->hasProp("@name"))
+        return -1;
+    if (!left->hasProp("@name"))
+        return 1;
+    return stricmp(right->queryProp("@name"), left->queryProp("@name"));  //fields without sequence alphabetical AFTER sequenced fields
+}
+
+bool WsEclWuInfo::getWsResource(const char *name, StringBuffer &out)
+{
+    if (strieq(name, "SOAP"))
+    {
+        out.appendf("<message name=\"%s\">", queryname.str());
+        Owned<IResultSetFactory> resultSetFactory = getResultSetFactory(username, password);
+        Owned<IConstWUWebServicesInfo> wsinfo = ensureWorkUnit()->getWebServicesInfo();
+        StringArray fields;
+        if (wsinfo)
+        {
+            SCMStringBuffer fieldList;
+            wsinfo->getText("fields", fieldList);
+            if (fieldList.length())
+                fields.appendListUniq(fieldList.str(), ",");
+        }
+        IArrayOf<IPropertyTree> parts;
+        if (fields.length())
+        {
+            ForEachItemIn(i, fields)
             {
-                meta->getColumnEclType(s, 0);
-                DisplayType dt = meta->getColumnDisplayType(0);
-                StringAttr ptype;
-                switch (dt)
-                {
-                case TypeBoolean:
-                    ptype.set("xsd:boolean");
-                    break;
-                case TypeInteger:
-                    ptype.set("xsd:integer");
-                    break;
-                case TypeUnsignedInteger:
-                    ptype.set("xsd:integer");
-                    break;
-                case TypeReal:
-                    ptype.set("xsd:real");
-                    break;
-                case TypeSet:
-                    ptype.set("tns:EspStringArray");
-                    break;
-                case TypeDataset:
-                case TypeData:
-                    ptype.set("tns:XmlDataSet");
-                    break;
-                case TypeUnicode:
-                case TypeString:
-                    ptype.set("xsd:string");
-                    break;
-                case TypeUnknown:
-                case TypeBeginIfBlock:
-                case TypeEndIfBlock:
-                case TypeBeginRecord:
-                default:
-                    ptype.set("xsd:string");
-                    break;
-                }
-                out.appendf("<part name=\"%s\" type=\"%s\" />", varname.str(), ptype.sget());
+                Owned<IConstWUResult> var = wu->getVariableByName(fields.item(i));
+                if (var)
+                    appendVariableParmInfo(parts, resultSetFactory, *var, i+1);
             }
         }
+        else
+        {
+            Owned<IConstWUResultIterator> vars = &ensureWorkUnit()->getVariables();
+            ForEach(*vars)
+                appendVariableParmInfo(parts, resultSetFactory, vars->query());
+        }
+        parts.sort(orderParts);
+        ForEachItemIn(i, parts)
+            toXML(&parts.item(i), out);
         out.append("</message>");
     }
 

+ 3 - 0
esp/xslt/wsecl3_form.xsl

@@ -1169,6 +1169,9 @@ function switchInputForm()
                 </xsl:variable>
                 <xsl:variable name="inputRows">
                     <xsl:choose>
+                        <xsl:when test="$annot/@formRows">
+                            <xsl:value-of select="$annot/@formRows"/>
+                        </xsl:when>
                         <xsl:when test="$maxoccurs='unbounded'">
                             <xsl:number value="4"/>
                         </xsl:when>

+ 58 - 0
testing/regress/ecl/formatstored.ecl

@@ -0,0 +1,58 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2014 HPCC Systems.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+//sequence should place these storeds in sensible order on the form, in xsd and wsdl
+//  field sizes should gradually increase fore each type on form
+
+unsigned2 u2 := 0 : stored('u2', few, format(fieldwidth(2), sequence(12)));
+integer2 i2 := 0 : stored('i2', format(fieldwidth(2), sequence(2)));
+unsigned7 u7 := 0 : stored('u7', format(fieldwidth(7), sequence(17)));
+integer3 i3 := 0 : stored('i3', format(fieldwidth(3), sequence(3)));
+integer1 i1 := 0 : stored('i1', format(fieldwidth(1), sequence(1)));
+unsigned6 u6 := 0 : stored('u6', format(fieldwidth(6), sequence(16)));
+integer6 i6 := 0 : stored('i6', format(fieldwidth(6), sequence(6)));
+unsigned1 u1 := 0 : stored('u1', format(fieldwidth(1), sequence(11)));
+integer7 i7 := 0 : stored('i7', format(fieldwidth(7), sequence(7)));
+integer8 i8 := 0 : stored('i8', format(fieldwidth(8), sequence(8)));
+string s1 := 'how now brown cow' : stored('s1', format(fieldwidth(40), fieldheight(10), sequence(20)));
+unsigned4 u4 := 0 : stored('u4', format(fieldwidth(4), sequence(14)));
+integer5 i5 := 0 : stored('i5', format(fieldwidth(5), sequence(5)));
+unsigned5 u5 := 0 : stored('u5', format(fieldwidth(5), sequence(15)));
+integer4 i4 := 0 : stored('i4', format(fieldwidth(4), sequence(4)));
+unsigned3 u3 := 0 : stored('u3', format(fieldwidth(3), sequence(13)));
+unsigned8 u8 := 0 : stored('u8', format(fieldwidth(8), sequence(18)));
+
+output (i1, named('i1'));
+output (i2, named('i2'));
+output (i3, named('i3'));
+output (i4, named('i4'));
+output (i5, named('i5'));
+output (i6, named('i6'));
+output (i7, named('i7'));
+output (i8, named('i8'));
+
+output (u1, named('u1'));
+output (u2, named('u2'));
+output (u3, named('u3'));
+output (u4, named('u4'));
+output (u5, named('u5'));
+output (u6, named('u6'));
+output (u7, named('u7'));
+output (u8, named('u8'));
+
+output (s1, named('s1'));
+

+ 51 - 0
testing/regress/ecl/key/formatstored.xml

@@ -0,0 +1,51 @@
+<Dataset name='i1'>
+ <Row><i1>0</i1></Row>
+</Dataset>
+<Dataset name='i2'>
+ <Row><i2>0</i2></Row>
+</Dataset>
+<Dataset name='i3'>
+ <Row><i3>0</i3></Row>
+</Dataset>
+<Dataset name='i4'>
+ <Row><i4>0</i4></Row>
+</Dataset>
+<Dataset name='i5'>
+ <Row><i5>0</i5></Row>
+</Dataset>
+<Dataset name='i6'>
+ <Row><i6>0</i6></Row>
+</Dataset>
+<Dataset name='i7'>
+ <Row><i7>0</i7></Row>
+</Dataset>
+<Dataset name='i8'>
+ <Row><i8>0</i8></Row>
+</Dataset>
+<Dataset name='u1'>
+ <Row><u1>0</u1></Row>
+</Dataset>
+<Dataset name='u2'>
+ <Row><u2>0</u2></Row>
+</Dataset>
+<Dataset name='u3'>
+ <Row><u3>0</u3></Row>
+</Dataset>
+<Dataset name='u4'>
+ <Row><u4>0</u4></Row>
+</Dataset>
+<Dataset name='u5'>
+ <Row><u5>0</u5></Row>
+</Dataset>
+<Dataset name='u6'>
+ <Row><u6>0</u6></Row>
+</Dataset>
+<Dataset name='u7'>
+ <Row><u7>0</u7></Row>
+</Dataset>
+<Dataset name='u8'>
+ <Row><u8>0</u8></Row>
+</Dataset>
+<Dataset name='s1'>
+ <Row><s1>how now brown cow</s1></Row>
+</Dataset>

+ 51 - 0
testing/regress/ecl/key/webservice.xml

@@ -0,0 +1,51 @@
+<Dataset name='i1'>
+ <Row><i1>0</i1></Row>
+</Dataset>
+<Dataset name='i2'>
+ <Row><i2>0</i2></Row>
+</Dataset>
+<Dataset name='i3'>
+ <Row><i3>0</i3></Row>
+</Dataset>
+<Dataset name='i4'>
+ <Row><i4>0</i4></Row>
+</Dataset>
+<Dataset name='i5'>
+ <Row><i5>0</i5></Row>
+</Dataset>
+<Dataset name='i6'>
+ <Row><i6>0</i6></Row>
+</Dataset>
+<Dataset name='i7'>
+ <Row><i7>0</i7></Row>
+</Dataset>
+<Dataset name='i8'>
+ <Row><i8>0</i8></Row>
+</Dataset>
+<Dataset name='u1'>
+ <Row><u1>0</u1></Row>
+</Dataset>
+<Dataset name='u2'>
+ <Row><u2>0</u2></Row>
+</Dataset>
+<Dataset name='u3'>
+ <Row><u3>0</u3></Row>
+</Dataset>
+<Dataset name='u4'>
+ <Row><u4>0</u4></Row>
+</Dataset>
+<Dataset name='u5'>
+ <Row><u5>0</u5></Row>
+</Dataset>
+<Dataset name='u6'>
+ <Row><u6>0</u6></Row>
+</Dataset>
+<Dataset name='u7'>
+ <Row><u7>0</u7></Row>
+</Dataset>
+<Dataset name='u8'>
+ <Row><u8>0</u8></Row>
+</Dataset>
+<Dataset name='s1'>
+ <Row><s1>how now brown cow</s1></Row>
+</Dataset>

+ 60 - 0
testing/regress/ecl/webservice.ecl

@@ -0,0 +1,60 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2014 HPCC Systems.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+string descrText := 'only the listed fields should show in form, xsd, and wsdl<br/>and in the given order<br/><br/>';
+string helpText := 'Enter some values and hit submit';
+
+#webservice(fields('u1', 'i1', 'u2', 'i2', 's1'), help(helpText), description(descrText));
+
+unsigned2 u2 := 0 : stored('u2', few, format(fieldwidth(2), sequence(12)));
+integer2 i2 := 0 : stored('i2', format(fieldwidth(2), sequence(2)));
+unsigned7 u7 := 0 : stored('u7', format(fieldwidth(7), sequence(17)));
+integer3 i3 := 0 : stored('i3', format(fieldwidth(3), sequence(3)));
+integer1 i1 := 0 : stored('i1', format(fieldwidth(1), sequence(1)));
+unsigned6 u6 := 0 : stored('u6', format(fieldwidth(6), sequence(16)));
+integer6 i6 := 0 : stored('i6', format(fieldwidth(6), sequence(6)));
+unsigned1 u1 := 0 : stored('u1', format(fieldwidth(1), sequence(11)));
+integer7 i7 := 0 : stored('i7', format(fieldwidth(7), sequence(7)));
+integer8 i8 := 0 : stored('i8', format(fieldwidth(8), sequence(8)));
+string s1 := 'how now brown cow' : stored('s1', format(fieldwidth(40), fieldheight(10), sequence(20)));
+unsigned4 u4 := 0 : stored('u4', format(fieldwidth(4), sequence(14)));
+integer5 i5 := 0 : stored('i5', format(fieldwidth(5), sequence(5)));
+unsigned5 u5 := 0 : stored('u5', format(fieldwidth(5), sequence(15)));
+integer4 i4 := 0 : stored('i4', format(fieldwidth(4), sequence(4)));
+unsigned3 u3 := 0 : stored('u3', format(fieldwidth(3), sequence(13)));
+unsigned8 u8 := 0 : stored('u8', format(fieldwidth(8), sequence(18)));
+
+output (i1, named('i1'));
+output (i2, named('i2'));
+output (i3, named('i3'));
+output (i4, named('i4'));
+output (i5, named('i5'));
+output (i6, named('i6'));
+output (i7, named('i7'));
+output (i8, named('i8'));
+
+output (u1, named('u1'));
+output (u2, named('u2'));
+output (u3, named('u3'));
+output (u4, named('u4'));
+output (u5, named('u5'));
+output (u6, named('u6'));
+output (u7, named('u7'));
+output (u8, named('u8'));
+
+output (s1, named('s1'));
+