Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

merged in commits from kaven276

  • Loading branch information...
commit 65c16888531aef24da088563455df6a89f4ad078 2 parents 024b343 + b533ac8
Vinay Pulim authored
View
66 Readme.md
@@ -5,6 +5,72 @@ Current limitations:
* Only a few XSD Schema types are supported
* Only WS-Security is supported using UsernameToken and PasswordText encoding
+## Update of kaven276's node-soap from milewise's node-soap
+
+### can parse multiRef request, use=encoding support
+
+ Soap request can use multiRef to mapping language's object model to xml, now node-soap can parse the request and mapping it to a javascript object that use object reference as multiRef.
+
+### Fiber integration, support async javascript service
+
+ For soap server, it has javascript service defined, when node-soap received and parse the soap request, it will route to call the javascript service function, and it will take the return value to convert to response soap xml. But the javascript service function may need to call some other file/network async functions to get the final return value, but node-soap will not wait for it, it just synchronously call and get return value. But this version integrate Fiber to support async service.
+
+ var SoapServices = {
+ 'ESyncNotifySPServiceService': {
+ 'ESyncNotifySP': {
+ 'eOrderRelationUpdateNotify': function(args) {
+ var fiber = Fiber.current;
+ var oraReqNV = args["eOrderRelationUpdateNotifyRequest"];
+ Request({
+ uri: cfg.psp_url + '.e',
+ method: 'post',
+ headers: {
+ 'Content-Type': "application/x-www-form-urlencoded"
+ },
+ body: QueryString.stringify(oraReqNV)
+ },
+ function(error, response, body) {
+ if (!error && response.statusCode === 200) {
+ fiber.run({
+ 'eOrderRelationUpdateNotifyResponse': {
+ 'RecordSequenceID': oraReqNV.RecordSequenceID,
+ 'ResultCode': body
+ }
+ });
+ } else {
+ fiber.run({
+ 'eOrderRelationUpdateNotifyResponse': {
+ 'RecordSequenceID': oraReqNV.RecordSequenceID,
+ 'ResultCode': 1
+ }
+ });
+ }
+ });
+ return yield();
+ }
+ }
+ }
+ }
+
+### correct method routing
+
+ How to determine the javascript service method? Firstly, use wsdl:service->wsdl:port->wsdlsoap:address.location to determine what binding/portType to use, Secondly, if style=RPC, then treat top element's name under soap body as the portType's method name,
+if style=RPC, then use http header SOAPAction to match the operation's soapAction, if there is no http header SOAPAction or no soapAction defined in wsdl, then check every method's input message's element name to find the match.
+
+### support both RPC/Document style request/response
+
+### xml namespace support
+
+ For WSDL parsing, different schema definitions will go into their respective schema parsed javascript objects, it allow the same named types/complexTypes/elements/... in different schemas. Formerly, different schema object with same name will just override and keep the last one, it's totally namespace ignorant and has potential serious problems.
+
+ For soap xml generation, the soap envelope has all the xmlns definition(exception for wsdl/soap itself related) defined in the wsdl that not only bring from the wsdl definition element, but from all of the wsdl and its import/include subs. But notice that if a xmlns defined in wsdl's element is conflict with definition element's xmlns map, it may have problems, but it's very rare case.
+
+ The soap request by client or response by server will give top element in soap:body a xml namespace prefix that is defined in envelope. And your javascript object for xml can have object attribute with xml namespace prefix, so the result xml payload will have the same namespace prefix. This way, you can use your javascript to make the right namespaced soap xml.
+
+### generate minimum of parsed javascript definition object
+
+ For the standard parsed format, remove any child/children when the child or child's useful information is attached to the parent's properties, remove fixed attribute of WSDL xml node, remove parent properties so there is no circular reference. The result is a clean and more clear parsed definition object.
+
## Install
Install with [npm](http://github.com/isaacs/npm):
View
35 lib/client.js
@@ -2,13 +2,18 @@
* Copyright (c) 2011 Vinay Pulim <vinay@milewise.com>
* MIT Licensed
*/
+
+function findKey(obj, val) {
+ for (var n in obj) if (obj[n] === val) return n;
+}
var http = require('./http'),
- assert = require('assert');
+ assert = require('assert'),
+ url = require('url');
-var Client = function(wsdl) {
+var Client = function(wsdl, endpoint) {
this.wsdl = wsdl;
- this._initializeServices(null);
+ this._initializeServices(endpoint);
}
Client.prototype.setEndpoint = function(endpoint) {
@@ -81,28 +86,29 @@ Client.prototype._invoke = function(method, arguments, location, callback) {
headers = {
SOAPAction: ((ns.lastIndexOf("/") != ns.length - 1) ? ns + "/" : ns) + name,
'Content-Type': "text/xml; charset=utf-8"
- },
- options = {};
-
+ };
+ options = {},
+ alias = findKey(defs.xmlns, ns);
+
// Allow the security object to add headers
if (self.security && self.security.addHeaders)
self.security.addHeaders(headers);
if (self.security && self.security.addOptions)
self.security.addOptions(options);
-
+
if (input.parts) {
assert.ok(!style || style == 'rpc', 'invalid message definition for document style binding');
- message = self.wsdl.objectToRpcXML(name, arguments);
- encoding = 'soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ';
+ message = self.wsdl.objectToRpcXML(name, arguments, alias, ns);
+ (method.inputSoap === 'encoded') && (encoding = 'soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ');
}
else {
assert.ok(!style || style == 'document', 'invalid message definition for rpc style binding');
- message = self.wsdl.objectToDocumentXML(input.$name, arguments);
+ message = self.wsdl.objectToDocumentXML(input.$name, arguments, input.targetNSAlias, input.targetNamespace);
}
xml = "<soap:Envelope " +
"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
encoding +
- "xmlns:ns0=\""+ns+"\">" +
+ this.wsdl.xmlnsInEnvelope + '>' +
"<soap:Header>" +
(self.security ? self.security.toXML() : "") +
"</soap:Header>" +
@@ -110,18 +116,19 @@ Client.prototype._invoke = function(method, arguments, location, callback) {
message +
"</soap:Body>" +
"</soap:Envelope>";
-
+ self.logger_req && self.logger_req(xml);
+
http.request(location, xml, function(err, response, body) {
if (err) {
callback(err);
}
else {
+ self.logger_res && self.logger_res(body);
try {
var obj = self.wsdl.xmlToObject(body);
}
catch (error) {
- callback(error, null, body);
- return;
+ return callback(error, null, body);
}
callback(null, obj[output.$name], body);
}
View
93 lib/server.js
@@ -3,6 +3,11 @@
* MIT Licensed
*/
+function findKey(obj, val) {
+ for (var n in obj) if (obj[n] === val) return n;
+}
+
+require('fibers');
var url = require('url'),
compress = null;
@@ -44,6 +49,9 @@ Server.prototype._requestListener = function(req, res) {
res.end();
}
else if (req.method === 'POST') {
+ res.setHeader('Content-Type', req.headers['content-type']);
+ //res.setHeader("Content-Type","application/soap+xml");
+ //res.setHeader("Encoding","utf-8");
var chunks = [], gunzip;
if (compress && req.headers["content-encoding"] == "gzip") {
gunzip = new compress.Gunzip;
@@ -55,18 +63,22 @@ Server.prototype._requestListener = function(req, res) {
})
req.on('end', function() {
var xml = chunks.join(''), result;
+ self.logger_req && self.logger_req(xml,res,req);
if (gunzip) {
gunzip.end();
gunzip = null
}
+ Fiber(function() {
try {
- result = self._process(xml);
+ result = self._process(xml, req.url);
+ self.logger_res && self.logger_res(result,res,req);
}
catch (err) {
result = err.stack;
}
res.write(result);
res.end();
+ }).run();
});
}
else {
@@ -74,50 +86,71 @@ Server.prototype._requestListener = function(req, res) {
}
}
-Server.prototype._process = function(input) {
+Server.prototype._process = function(input,URL) {
+ var pathname = url.parse(URL).pathname.replace(/\/$/,'');
var self = this,
obj = this.wsdl.xmlToObject(input),
- messageName = Object.keys(obj)[0],
- args = obj[messageName],
- portTypes = this.wsdl.definitions.portTypes;
+ bindings = this.wsdl.definitions.bindings, binding,
+ methods, method, methodName;
+ var serviceName, portName;
+
+ // use port.location and current url to find the right binding
+ binding = (function(self){
+ var services = self.wsdl.definitions.services;
+ for(serviceName in services ) {
+ var service = services[serviceName];
+ var ports = service.ports;
+ for(portName in ports) {
+ var port = ports[portName];
+ var portPathname = url.parse(port.location).pathname.replace(/\/$/,'');
+ if(portPathname===pathname)
+ return port.binding;
+ }
+ }
+ })(this);
- for (var portName in portTypes) {
- var portType = portTypes[portName];
- var methods = portType.methods;
- for (var methodName in methods) {
- var method = methods[methodName];
- if (method.input.$name == messageName) {
- return self._executeMethod(methodName, method.output.$name, args);
- }
- }
- }
+ methods = binding.methods;
+ if(binding.style === 'rpc') {
+ methodName = Object.keys(obj)[0];
+ return self._executeMethod(serviceName, portName, methodName, methodName+'Response', obj[methodName], 'rpc');
+ } else {
+ var messageElemName = Object.keys(obj)[0];
+ var pair = binding.topElements[messageElemName];
+ return self._executeMethod(serviceName, portName, pair.methodName, pair.outputName, obj[messageElemName], 'document');
+ }
+
return '';
}
-Server.prototype._executeMethod = function(methodName, outputName, args) {
- var services = this.services;
- for (var serviceName in services) {
- var service = services[serviceName];
- for (var portName in service) {
- var port = service[portName];
- var method = port[methodName];
- if (method) {
- var body = this.wsdl.objectToDocumentXML(outputName, method(args))
- return this._envelope(body);
- }
- }
+Server.prototype._executeMethod = function(serviceName, portName, methodName, outputName, args, style) {
+ var method, body;
+ try {
+ method = this.services[serviceName][portName][methodName];
+ } catch(e) {
+ return this._envelope(body);
}
- return this._envelope();
+ if(style==='rpc') {
+ body = this.wsdl.objectToRpcXML(outputName, method(args), '', this.wsdl.definitions.$targetNamespace);
+ } else {
+ var element = this.wsdl.definitions.services[serviceName].ports[portName].binding.methods[methodName].output;
+ body = this.wsdl.objectToDocumentXML(outputName, method(args), element.targetNSAlias, element.targetNamespace);
+ }
+ return this._envelope(body);
}
Server.prototype._envelope = function(body) {
+ var defs = this.wsdl.definitions,
+ ns = defs.$targetNamespace,
+ encoding = '',
+ alias = findKey(defs.xmlns, ns);
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
- "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
+ "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
+ encoding +
+ this.wsdl.xmlnsInEnvelope + '>' +
"<soap:Body>" +
body +
"</soap:Body>" +
"</soap:Envelope>";
-
return xml;
}
View
5 lib/soap.js
@@ -9,6 +9,7 @@ var Client = require('./client').Client,
crypto = require('crypto'),
WSDL = require('./wsdl').WSDL;
+var WSDL = require('./wsdl').WSDL;
var _wsdlCache = {};
function _requestWSDL(url, callback) {
@@ -24,9 +25,9 @@ function _requestWSDL(url, callback) {
}
}
-function createClient(url, callback) {
+function createClient(url, callback, endpoint) {
_requestWSDL(url, function(err, wsdl) {
- callback(err, wsdl && new Client(wsdl));
+ callback(err, wsdl && new Client(wsdl, endpoint));
})
}
View
739 lib/wsdl.js
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (c) 2011 Vinay Pulim <vinay@milewise.com>
* MIT Licensed
*/
@@ -44,16 +44,27 @@ function trim(text) {
return text.replace(trimLeft, '').replace(trimRight, '');
}
-var Element = function(nsName, attrs, parent) {
+function extend(base, obj) {
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ base[key] = obj[key];
+ }
+ }
+ return base;
+}
+
+function findKey(obj, val) {
+ for (var n in obj) if (obj[n] === val) return n;
+}
+
+var Element = function(nsName, attrs) {
var parts = splitNSName(nsName);
this.nsName = nsName;
this.namespace = parts.namespace;
this.name = parts.name;
- this.attrs = attrs;
this.children = [];
- this.parent = parent;
- this.xmlns = {}
+ this.xmlns = {};
for (var key in attrs) {
var match = /^xmlns:?(.*)$/.exec(key);
if (match) {
@@ -64,129 +75,159 @@ var Element = function(nsName, attrs, parent) {
}
}
}
-
-Element.prototype.allowedChildren = null;
+Element.prototype.deleteFixedAttrs = function() {
+ this.children && this.children.length === 0 && delete this.children;
+ this.xmlns && Object.keys(this.xmlns).length === 0 && delete this.xmlns;
+ delete this.nsName;
+ delete this.namespace;
+ delete this.name;
+}
+Element.prototype.allowedChildren = [];
Element.prototype.startElement= function(stack, nsName, attrs) {
if (!this.allowedChildren) return;
- var childClass = this.allowedChildren()[splitNSName(nsName).name],
+ var childClass = this.allowedChildren[splitNSName(nsName).name],
element = null;
if (childClass) {
- stack.push(new childClass(nsName, attrs, this));
+ stack.push(new childClass(nsName, attrs));
}
else {
- // this.unexpected(nsName);
+ this.unexpected(nsName);
}
+
}
Element.prototype.endElement = function(stack, nsName) {
if (this.nsName === nsName) {
- var parent = this.parent;
- if (parent) {
+ if(stack.length < 2 ) return;
+ var parent = stack[stack.length - 2];
+ if (this !== stack[0]) {
+ extend(stack[0].xmlns, this.xmlns);
+ // delete this.xmlns;
parent.children.push(this);
parent.addChild(this);
- };
+ } else {
+ console.log(nsName, this.nsName);
+ }
stack.pop();
+ } else {
+ console.log(nsName, this.nsName);
}
}
-Element.prototype.addChild = function(child) { }
+Element.prototype.addChild = function(child) { return;console.log(this.nsName + ' addChild for ' + child.nsName); }
Element.prototype.unexpected = function(name) {
throw new Error('Found unexpected element (' + name + ') inside ' + this.nsName);
}
Element.prototype.description = function(definitions) {
return this.$name || this.name;
}
-
-var SimpleTypeElement = function() { Element.apply(this, arguments); }
-inherits(SimpleTypeElement, Element);
-
-var ComplexTypeElement = function() { Element.apply(this, arguments); }
-inherits(ComplexTypeElement, Element);
-ComplexTypeElement.prototype.allowedChildren = function() {
- return {
- annotation: Element,
- sequence: SequenceElement
+Element.prototype.init = function() {};
+Element.createSubClass = function() {
+ var root = this;
+ var subElement = function() {
+ root.apply(this, arguments);
+ this.init();
};
-}
-ComplexTypeElement.prototype.description = function(definitions) {
- var children = this.children;
- for (var i=0, child; child=children[i]; i++) {
- if (child instanceof SequenceElement)
- return child.description(definitions);
- }
- return {};
-}
-
-var ElementElement = function() { Element.apply(this, arguments); }
-inherits(ElementElement, Element);
-ElementElement.prototype.allowedChildren = function() {
- return {
- annotation: Element,
- complexType: ComplexTypeElement
- };
-}
-ElementElement.prototype.description = function(definitions) {
- var element = {},
- name = this.$name;
- if (this.$minOccurs !== this.$maxOccurs) {
- name += '[]';
- }
+ // inherits(subElement, root);
+ subElement.prototype.__proto__ = root.prototype;
+ return subElement;
+}
+
+
+var ElementElement = Element.createSubClass();
+var InputElement = Element.createSubClass();
+var OutputElement = Element.createSubClass();
+var SimpleTypeElement = Element.createSubClass();
+var ComplexTypeElement = Element.createSubClass();
+var SequenceElement = Element.createSubClass();
+var MessageElement = Element.createSubClass();
+
+var SchemaElement = Element.createSubClass();
+var TypesElement = Element.createSubClass();
+var OperationElement = Element.createSubClass();
+var PortTypeElement = Element.createSubClass();
+var BindingElement = Element.createSubClass();
+var PortElement = Element.createSubClass();
+var ServiceElement = Element.createSubClass();
+var DefinitionsElement = Element.createSubClass();
+
+var ElementTypeMap = {
+ types: [TypesElement, 'schema'],
+ schema: [SchemaElement, 'element complexType simpleType include import'],
+ element: [ElementElement, 'annotation complexType'],
+ simpleType: [SimpleTypeElement, ''],
+ complexType: [ComplexTypeElement, 'annotation sequence'],
+ sequence: [SequenceElement, 'element'],
- if (this.$type) {
- var typeName = splitNSName(this.$type).name,
- typeElement = definitions.complexTypes[typeName] || definitions.types[typeName];
- if (typeElement && !(typeName in Primitives)) {
- element[name] = typeElement.description(definitions);
- }
- else
- element[name] = this.$type;
- }
- else {
- var children = this.children;
- element[name] = {};
- for (var i=0, child; child=children[i]; i++) {
- if (child instanceof ComplexTypeElement)
- element[name] = child.description(definitions);
- }
- }
- return element;
-}
+ service: [ServiceElement, 'port documentation'],
+ port: [PortElement, 'address'],
+ binding: [BindingElement, '_binding SecuritySpec operation'],
+ portType: [PortTypeElement, 'operation'],
+ message: [MessageElement, 'part'],
+ operation: [OperationElement, 'documentation input output _operation'],
+ input : [InputElement, 'body'],
+ output : [OutputElement, 'body'],
+ definitions: [DefinitionsElement, 'types message portType binding service']
+};
-var SequenceElement = function() { Element.apply(this, arguments); }
-inherits(SequenceElement, Element);
-SequenceElement.prototype.allowedChildren = function() {
- return {
- element: ElementElement
- };
+function mapElementTypes(types) {
+ var types = types.split(' ');
+ var rtn = {}
+ types.forEach(function(type){
+ rtn[type.replace(/^_/,'')] = (ElementTypeMap[type] || [Element]) [0];
+ });
+ return rtn;
}
-SequenceElement.prototype.description = function(definitions) {
- var children = this.children;
- var sequence = {};
- for (var i=0, child; child=children[i]; i++) {
- var description = child.description(definitions);
- for (var key in description) {
- sequence[key] = description[key];
- }
- }
- return sequence;
+
+for(n in ElementTypeMap) {
+ var v = ElementTypeMap[n];
+ v[0].prototype.allowedChildren = mapElementTypes(v[1]);
}
-var SchemaElement = function() {
- Element.apply(this, arguments);
+MessageElement.prototype.init = function() {
+ this.element = null;
+ this.parts = null;
+}
+SchemaElement.prototype.init = function() {
this.complexTypes = {};
this.types = {};
+ this.elements = {};
this.includes = [];
}
-inherits(SchemaElement, Element);
-SchemaElement.prototype.allowedChildren = function() {
- return {
- element: ElementElement,
- complexType: ComplexTypeElement,
- simpleType: SimpleTypeElement,
- include: Element,
- import: Element
- }
+TypesElement.prototype.init = function() {
+ this.schemas = {};
}
+OperationElement.prototype.init = function() {
+ this.input = null;
+ this.output = null;
+ this.inputSoap = null;
+ this.outputSoap = null;
+ this.style = '';
+ this.soapAction = '';
+}
+PortTypeElement.prototype.init = function() {
+ this.methods = {};
+}
+BindingElement.prototype.init = function() {
+ this.transport = '';
+ this.style = '';
+ this.methods = {};
+}
+PortElement.prototype.init = function() {
+ this.location = null;
+}
+ServiceElement.prototype.init = function() {
+ this.ports = {};
+}
+DefinitionsElement.prototype.init = function() {
+ if (this.name !== 'definitions') this.unexpected(nsName);
+ this.messages = {};
+ this.portTypes = {};
+ this.bindings = {};
+ this.services = {};
+ this.schemas = {};
+}
+
SchemaElement.prototype.addChild = function(child) {
if (child.$name in Primitives) return;
if (child.name === 'include' || child.name === 'import') {
@@ -195,92 +236,122 @@ SchemaElement.prototype.addChild = function(child) {
else if (child.name === 'complexType') {
this.complexTypes[child.$name] = child;
}
+ else if (child.name === 'element') {
+ this.elements[child.$name] = child;
+ }
else if (child.$name) {
this.types[child.$name] = child;
}
+ this.children.pop();
+ // child.deleteFixedAttrs();
}
-
-function extend(base, obj) {
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- base[key] = obj[key];
+TypesElement.prototype.addChild = function(child) {
+ assert(child instanceof SchemaElement);
+ this.schemas[child.$targetNamespace] = child;
+}
+InputElement.prototype.addChild = function(child) {
+ if (child.name === 'body') {
+ this.use = child.$use;
+ if (this.use === 'encoded') {
+ this.encodingStyle = child.$encodingStyle;
}
+ this.children.pop();
+ }
+}
+OutputElement.prototype.addChild = function(child) {
+ if (child.name === 'body') {
+ this.use = child.$use;
+ if (this.use === 'encoded') {
+ this.encodingStyle = child.$encodingStyle;
+ }
+ this.children.pop();
+ }
+}
+OperationElement.prototype.addChild = function(child) {
+ if (child.name === 'operation') {
+ this.soapAction = child.$soapAction || '';
+ this.style = child.$style || '';
+ this.children.pop();
}
- return base;
}
-
-var TypesElement = function() {
- Element.apply(this, arguments);
- this.complexTypes = {};
- this.types = {};
- this.includes = [];
+BindingElement.prototype.addChild = function(child) {
+ if (child.name === 'binding') {
+ this.transport = child.$transport;
+ this.style = child.$style;
+ this.children.pop();
+ }
}
-inherits(TypesElement, Element);
-TypesElement.prototype.allowedChildren = function() {
- return { schema: SchemaElement };
+PortElement.prototype.addChild = function(child) {
+ if (child.name === 'address') {
+ this.location = child.$location;
+ }
}
-TypesElement.prototype.addChild = function(child) {
- assert(child instanceof SchemaElement);
- extend(this.complexTypes, child.complexTypes);
- extend(this.types, child.types);
- extend(this.includes, child.includes);
+DefinitionsElement.prototype.addChild = function(child) {
+ var self = this;
+ if (child instanceof TypesElement) {
+ self.schemas = child.schemas;
+ }
+ else if (child instanceof MessageElement) {
+ self.messages[child.$name] = child;
+ }
+ else if (child instanceof PortTypeElement) {
+ self.portTypes[child.$name] = child;
+ }
+ else if (child instanceof BindingElement) {
+ if (child.transport === 'http://schemas.xmlsoap.org/soap/http' ||
+ child.transport === 'http://www.w3.org/2003/05/soap/bindings/HTTP/')
+ self.bindings[child.$name] = child;
+ }
+ else if (child instanceof ServiceElement) {
+ self.services[child.$name] = child;
+ }
+ else {
+ assert(false, "Invalid child type");
+ }
+ this.children.pop();
}
-var MessageElement = function() {
- Element.apply(this, arguments);
- this.element = null;
- this.parts = null;
-}
-inherits(MessageElement, Element);
-MessageElement.prototype.allowedChildren = function() {
- return { part: Element };
-}
+
MessageElement.prototype.postProcess = function(definitions) {
var part = this.children[0];
if (!part) return;
-
+
assert(part.name === 'part', 'Expected part element');
if (part.$element) {
- var nsName = splitNSName(part.$element)
- this.element = definitions.types[nsName.name];
- this.ns = nsName.namespace;
+ delete this.parts;
+ var nsName = splitNSName(part.$element);
+ var ns = nsName.namespace;
+ this.element = definitions.schemas[definitions.xmlns[ns]].elements[nsName.name];
+ this.element.targetNSAlias = ns;
+ this.element.targetNamespace = definitions.xmlns[ns];
+ this.children.splice(0,1);
}
else {
// rpc encoding
this.parts = {};
+ delete this.element;
for (var i=0, part; part = this.children[i]; i++) {
assert(part.name === 'part', 'Expected part element');
- this.parts[part.$name] = part.$type;
+ var nsName = splitNSName(part.$type);
+ var ns = definitions.xmlns[nsName.namespace];
+ var type = nsName.name;
+ this.parts[part.$name] = definitions.schemas[ns].types[type] || definitions.schemas[ns].complexTypes[type];
+ this.parts[part.$name].namespace = nsName.namespace;
+ this.parts[part.$name].xmlns = ns;
+ this.children.splice(i--,1);
}
}
+ this.deleteFixedAttrs();
}
-MessageElement.prototype.description = function(definitions) {
- if (this.element) {
- return this.element && this.element.description(definitions);
- }
- var desc = {};
- desc[this.$name] = this.parts;
- return desc;
-}
-
-var OperationElement = function() {
- Element.apply(this, arguments);
- this.input = null;
- this.output = null;
-}
-inherits(OperationElement, Element);
-OperationElement.prototype.allowedChildren = function() {
- return {
- documentation: Element,
- input: Element,
- output: Element,
- operation: Element
- };
-}
-OperationElement.prototype.postProcess = function(definitions) {
+OperationElement.prototype.postProcess = function(definitions, tag) {
var children = this.children;
for (var i=0, child; child=children[i]; i++) {
if (child.name !== 'input' && child.name !== 'output') continue;
+ if(tag === 'binding') {
+ this[child.name] = child;
+ children.splice(i--,1);
+ continue;
+ }
var messageName = splitNSName(child.$message).name;
var message = definitions.messages[messageName]
message.postProcess(definitions);
@@ -291,107 +362,51 @@ OperationElement.prototype.postProcess = function(definitions) {
else {
this[child.name] = message;
}
+ children.splice(i--,1);
}
-}
-OperationElement.prototype.description = function(definitions) {
- var inputDesc = this.input.description(definitions);
- var outputDesc = this.output.description(definitions);
- return {
- input: inputDesc && inputDesc[Object.keys(inputDesc)[0]],
- output: outputDesc && outputDesc[Object.keys(outputDesc)[0]]
- }
-}
-
-var PortTypeElement = function() {
- Element.apply(this, arguments);
- this.methods = {};
-}
-inherits(PortTypeElement, Element);
-PortTypeElement.prototype.allowedChildren = function() {
- return { operation: OperationElement };
+ this.deleteFixedAttrs();
}
PortTypeElement.prototype.postProcess = function(definitions) {
- var children = this.children;
+ var children = this.children;
for (var i=0, child; child=children[i]; i++) {
if (child.name != 'operation') continue;
- child.postProcess(definitions);
+ child.postProcess(definitions, 'portType');
this.methods[child.$name] = child;
+ children.splice(i--,1);
}
-}
-PortTypeElement.prototype.description = function(definitions) {
- var methods = {};
- for (var name in this.methods) {
- var method = this.methods[name];
- methods[name] = method.description(definitions);
- }
- return methods;
-}
-
-var BindingElement = function() {
- Element.apply(this, arguments);
- this.transport = null;
- this.style = null;
- this.methods = {};
-}
-inherits(BindingElement, Element);
-BindingElement.prototype.allowedChildren = function() {
- return {
- binding: Element,
- SecuritySpec: Element,
- operation: Element
- };
-}
-BindingElement.prototype.addChild = function(child) {
- if (child.name === 'binding') {
- this.transport = child.$transport;
- this.style = child.$style;
- }
+ delete this.$name;
+ this.deleteFixedAttrs();
}
BindingElement.prototype.postProcess = function(definitions) {
var type = splitNSName(this.$type).name,
portType = definitions.portTypes[type],
- style = this.style;
-
+ style = this.style,
+ children = this.children;
+
portType.postProcess(definitions);
this.methods = portType.methods;
- for (var name in this.methods) {
- this.methods[name].style = style;
- }
-}
-BindingElement.prototype.description = function(definitions) {
- var methods = {};
- for (var name in this.methods) {
- var method = this.methods[name];
- methods[name] = method.description(definitions);
- }
- return methods;
-}
-
-var PortElement = function() {
- Element.apply(this, arguments);
- this.location = null;
-}
-inherits(PortElement, Element);
-
-PortElement.prototype.allowedChildren = function() {
- return { address: Element };
-}
-PortElement.prototype.addChild = function(child) {
- if (child.name === 'address') {
- this.location = child.$location;
- }
-}
-
-var ServiceElement = function() {
- Element.apply(this, arguments);
- this.ports = {};
-}
-inherits(ServiceElement, Element);
-ServiceElement.prototype.allowedChildren = function() {
- return {
- port: PortElement,
- documentation: Element
- };
+ // delete portType.methods; both binding and portType should keep the same set of operations
+
+ for (var i=0, child; child=children[i]; i++) {
+ if (child.name != 'operation') continue;
+ child.postProcess(definitions, 'binding');
+ children.splice(i--,1);
+ child.style || (child.style = style);
+ var method = this.methods[child.$name];
+ method.style = child.style;
+ method.soapAction = child.soapAction;
+ method.inputSoap = child.input || null;
+ method.outputSoap = child.output || null;
+ method.inputSoap && method.inputSoap.deleteFixedAttrs();
+ method.outputSoap && method.outputSoap.deleteFixedAttrs();
+ // delete method.$name; client will use it to make right request for top element name in body
+ // method.deleteFixedAttrs(); why ???
+ }
+
+ delete this.$name;
+ delete this.$type;
+ this.deleteFixedAttrs();
+
}
ServiceElement.prototype.postProcess = function(definitions) {
var children = this.children,
@@ -406,65 +421,105 @@ ServiceElement.prototype.postProcess = function(definitions) {
location: child.location,
binding: binding
}
+ children.splice(i--,1);
}
}
-}
-ServiceElement.prototype.description = function(definitions) {
- var ports = {};
- for (var name in this.ports) {
- var port = this.ports[name];
- ports[name] = port.binding.description(definitions);
- }
- return ports;
+ delete this.$name;
+ this.deleteFixedAttrs();
}
-var DefinitionsElement = function() {
- Element.apply(this, arguments);
- if (this.name !== 'definitions') this.unexpected(nsName);
- this.complexTypes = {};
- this.types = {};
- this.messages = {};
- this.portTypes = {};
- this.bindings = {};
- this.services = {};
-}
-inherits(DefinitionsElement, Element);
-DefinitionsElement.prototype.allowedChildren = function() {
- return {
- types: TypesElement,
- message: MessageElement,
- portType: PortTypeElement,
- binding: BindingElement,
- service: ServiceElement
- };
+ComplexTypeElement.prototype.description = function(definitions) {
+ var children = this.children;
+ for (var i=0, child; child=children[i]; i++) {
+ if (child instanceof SequenceElement)
+ return child.description(definitions);
+ }
+ return {};
}
-DefinitionsElement.prototype.addChild = function(child) {
- var self = this;
- if (child instanceof TypesElement) {
- self.complexTypes = child.complexTypes;
- self.types = child.types;
- self.includes = child.includes;
+ElementElement.prototype.description = function(definitions) {
+ var element = {},
+ name = this.$name,
+ schema;
+ if (this.$minOccurs !== this.$maxOccurs) {
+ name += '[]';
}
- else if (child instanceof MessageElement) {
- self.messages[child.$name] = child;
+
+ if (this.$type) {
+ var typeName = splitNSName(this.$type).name,
+ ns = definitions.xmlns[splitNSName(this.$type).namespace],
+ schema = definitions.schemas[ns],
+ typeElement = schema && ( schema.complexTypes[typeName] || schema.types[typeName] );
+ if (typeElement && !(typeName in Primitives)) {
+ element[name] = typeElement.description(definitions);
+ }
+ else
+ element[name] = this.$type;
}
- else if (child instanceof PortTypeElement) {
- self.portTypes[child.$name] = child;
+ else {
+ var children = this.children;
+ element[name] = {};
+ for (var i=0, child; child=children[i]; i++) {
+ if (child instanceof ComplexTypeElement)
+ element[name] = child.description(definitions);
+ }
}
- else if (child instanceof BindingElement) {
- if (child.transport === 'http://schemas.xmlsoap.org/soap/http' ||
- child.transport === 'http://www.w3.org/2003/05/soap/bindings/HTTP/')
- self.bindings[child.$name] = child;
+ return element;
+}
+SequenceElement.prototype.description = function(definitions) {
+ var children = this.children;
+ var sequence = {};
+ for (var i=0, child; child=children[i]; i++) {
+ var description = child.description(definitions);
+ for (var key in description) {
+ sequence[key] = description[key];
+ }
}
- else if (child instanceof ServiceElement) {
- self.services[child.$name] = child;
+ return sequence;
+}
+MessageElement.prototype.description = function(definitions) {
+ if (this.element) {
+ return this.element && this.element.description(definitions);
}
- else {
- assert(false, "Invalid child type");
+ var desc = {};
+ desc[this.$name] = this.parts;
+ return desc;
+}
+PortTypeElement.prototype.description = function(definitions) {
+ var methods = {};
+ for (var name in this.methods) {
+ var method = this.methods[name];
+ methods[name] = method.description(definitions);
}
+ return methods;
+}
+OperationElement.prototype.description = function(definitions) {
+ return;
+ var inputDesc = this.input.description(definitions);
+ var outputDesc = this.output.description(definitions);
+ return {
+ input: inputDesc && inputDesc[Object.keys(inputDesc)[0]],
+ output: outputDesc && outputDesc[Object.keys(outputDesc)[0]]
+ }
+}
+BindingElement.prototype.description = function(definitions) {
+ var methods = {};
+ for (var name in this.methods) {
+ var method = this.methods[name];
+ methods[name] = method.description(definitions);
+ }
+ return methods;
+}
+ServiceElement.prototype.description = function(definitions) {
+ var ports = {};
+ for (var name in this.ports) {
+ var port = this.ports[name];
+ ports[name] = port.binding.description(definitions);
+ }
+ return ports;
}
+
var WSDL = function(definition, uri) {
var self = this,
fromFunc;
@@ -484,15 +539,40 @@ var WSDL = function(definition, uri) {
process.nextTick(function() {
fromFunc.call(self, definition);
+// console.log(self.definitions);
self.processIncludes(function(err) {
+ self.definitions.deleteFixedAttrs();
var services = self.services = self.definitions.services ;
if (services) {
for (var name in services) {
services[name].postProcess(self.definitions);
}
}
+ var complexTypes = self.definitions.complexTypes;
+ if (complexTypes) {
+ for (var name in complexTypes) {
+ complexTypes[name].deleteFixedAttrs();
+ }
+ }
self.callback(err, self);
});
+
+ // prepare soap envelope xmlns definition string
+ self.xmlnsInEnvelope = self._xmlnsMap();
+
+ // for document style, for every binding, prepare input message element name to (methodName, output message element name) mapping
+ var bindings = self.definitions.bindings;
+ for(var bindingName in bindings) {
+ var binding = bindings[bindingName];
+ if(binding.style !== 'document') continue;
+ var methods = binding.methods;
+ var topEls = binding.topElements = {};
+ for(methodName in methods) {
+ var inputName = methods[methodName].input.$name;
+ var outputName = methods[methodName].output.$name;
+ topEls[inputName] = {"methodName": methodName, "outputName": outputName};
+ }
+ }
})
}
@@ -502,7 +582,7 @@ WSDL.prototype.onReady = function(callback) {
WSDL.prototype.processIncludes = function(callback) {
var self = this,
- uri = this.definitions && this.definitions.includes.shift();
+ uri = this.definitions && this.definitions.includes && this.definitions.includes.shift();
if (!uri) {
callback(null, this);
@@ -550,26 +630,45 @@ WSDL.prototype.xmlToObject = function(xml) {
Body: {
Fault: { faultcode: 'string', faultstring: 'string', detail: 'string' }}}},
stack = [{name: null, object: root, schema: schema}];
-
+
+ var refs = {}, id; // {id:{hrefs:[],obj:}, ...}
p.on('startElement', function(nsName, attrs) {
var name = splitNSName(nsName).name,
top = stack[stack.length-1],
topSchema = top.schema,
obj = {};
-
if (!objectName && top.name === 'Body' && name !== 'Fault') {
var message = self.definitions.messages[name];
+ if(!message) {
+ // or name = name + "Request directly"
+ var portTypes = self.definitions.portTypes;
+ for(var n in portTypes) {
+ var portType = portTypes[n];
+ break;
+ }
+ message = self.definitions.messages[portType.methods[name].input.$name];
+ }
topSchema = message.description(self.definitions);
objectName = name;
}
+
+ if(attrs.href) {
+ id = attrs.href.substr(1);
+ if(!refs[id]) refs[id] = {hrefs:[],obj:null};
+ refs[id].hrefs.push({par:top.object,key:name});
+ }
+ if(id=attrs.id) {
+ if(!refs[id]) refs[id] = {hrefs:[],obj:null};
+ }
if (topSchema && topSchema[name+'[]']) name = name + '[]';
- stack.push({name: name, object: obj, schema: topSchema && topSchema[name]});
+ stack.push({name: name, object: obj, schema: topSchema && topSchema[name], id:attrs.id});
})
p.on('endElement', function(nsName) {
- var obj = stack.pop().object,
+ var cur = stack.pop(),
+ obj = cur.object,
top = stack[stack.length-1],
topObject = top.object,
topSchema = top.schema,
@@ -589,6 +688,10 @@ WSDL.prototype.xmlToObject = function(xml) {
else {
topObject[name] = obj;
}
+
+ if(cur.id) {
+ refs[cur.id].obj = obj;
+ }
})
p.on('text', function(text) {
@@ -616,6 +719,15 @@ WSDL.prototype.xmlToObject = function(xml) {
if (!p.parse(xml, false)) {
throw new Error(p.getError());
}
+
+ for(var n in refs) {
+ var ref = refs[n];
+ var obj = ref.obj;
+ ref.hrefs.forEach(function(href) {
+ href.par[href.key] = obj;
+ });
+ }
+
var body = root.Envelope.Body;
if (body.Fault) {
throw new Error(body.Fault.faultcode+': '+body.Fault.faultstring+(body.Fault.detail ? ': ' + body.Fault.detail : ''));
@@ -623,30 +735,25 @@ WSDL.prototype.xmlToObject = function(xml) {
return body;
}
-WSDL.prototype.objectToDocumentXML = function(name, params) {
- var args = {}, ns, xmlns;
+WSDL.prototype.objectToDocumentXML = function(name, params, ns, xmlns) {
+ var args = {};
args[name] = params;
- var message = this.definitions.messages[name];
- if (message && message.ns) {
- ns = message.ns;
- xmlns = this.definitions.xmlns[ns];
- }
return this.objectToXML(args, null, ns, xmlns);
}
-WSDL.prototype.objectToRpcXML = function(name, params, namespace) {
+WSDL.prototype.objectToRpcXML = function(name, params, namespace, xmlns) {
var self = this,
parts = [],
- namespace = namespace || 'ns0',
- xmlns = xmlns || '',
+ defs = this.definitions,
+ namespace = namespace || findKey(defs.xmlns, xmlns),
+ xmlns = xmlns || defs.xmlns[namespace],
nsAttrName = '_xmlns';
-
- parts.push(['<',namespace,':',name,xmlns,'>'].join(''));
+ parts.push(['<',namespace,':',name,'>'].join(''));
for (var key in params) {
if (key != nsAttrName) {
var value = params[key];
parts.push(['<',key,'>'].join(''));
- parts.push(xmlEscape(value));
+ parts.push((typeof value==='object')?this.objectToXML(value):xmlEscape(value));
parts.push(['</',key,'>'].join(''));
}
}
@@ -658,7 +765,7 @@ WSDL.prototype.objectToRpcXML = function(name, params, namespace) {
WSDL.prototype.objectToXML = function(obj, name, namespace, xmlns) {
var self = this,
parts = [],
- xmlnsAttrib = xmlns ? ' xmlns:'+namespace+'="'+xmlns+'"' : '',
+ xmlnsAttrib = false ? ' xmlns:'+namespace+'="'+xmlns+'"'+' xmlns="'+xmlns+'"' : '',
ns = namespace ? namespace + ':' : '';
if (Array.isArray(obj)) {
@@ -734,6 +841,30 @@ WSDL.prototype._fromServices = function(services) {
}
+
+
+WSDL.prototype._xmlnsMap = function() {
+ var xmlns = this.definitions.xmlns;
+ var str = '';
+ for (var alias in xmlns) {
+ if (alias === '') continue;
+ var ns = xmlns[alias];
+ switch(ns) {
+ case "http://xml.apache.org/xml-soap" : // apachesoap
+ case "http://schemas.xmlsoap.org/wsdl/" : // wsdl
+ case "http://schemas.xmlsoap.org/wsdl/soap/" : // wsdlsoap
+ case "http://schemas.xmlsoap.org/soap/encoding/" : // soapenc
+ case "http://www.w3.org/2001/XMLSchema" : // xsd
+ continue;
+ }
+ if (~ns.indexOf('http://schemas.xmlsoap.org/')) continue;
+ if (~ns.indexOf('http://www.w3.org/')) continue;
+ if (~ns.indexOf('http://xml.apache.org/')) continue;
+ str += ' xmlns:' + alias + '="' + ns + '"';
+ }
+ return str;
+}
+
function open_wsdl(uri, callback) {
var wsdl;
if (!/^http/.test(uri)) {
View
1  package.json
@@ -7,6 +7,7 @@
"dependencies": {
"node-expat": ">= 1.3.0",
"request": "=2.2.6"
+ ,"fibers": "0.6.4"
},
"repository" : {
"type":"git",
View
8 test/ip2tele/cfg.js
@@ -0,0 +1,8 @@
+var cfg = {
+ 'ServerID': 'tjcc',
+ fake_port: '8008',
+ wsdl_file: require('path').resolve("./ip2tele.wsdl"),
+ path: '/webservice_iuim/services/QueryUserInfoServiceApply',
+ site_port: 8012
+}
+module.exports = cfg;
View
89 test/ip2tele/fake_server.js
@@ -0,0 +1,89 @@
+var cfg = require('./cfg.js');
+var soap = require('../..');
+var path = require('path');
+var http = require('http');
+var QueryString = require('querystring');
+var Request = require('request');
+var moment = require('moment');
+var fs = require('fs');
+
+var SoapServices = {
+ 'QueryUserInfoServiceApply': {
+ 'QueryUserInfoServiceApplyHttpPort': {
+ 'QueryUserInfoServiceApply': function(args) {
+ var fiber = Fiber.current;
+ console.log('received args = ');
+ console.log(args);
+ return ({
+ 'tns:ServerInfo': {
+ 'ResultCode': '0',
+ 'Description': 'success'
+ },
+ 'tns:UserInfo': {
+ 'UserName': '15620001781'
+ }
+ });
+ }
+ }
+ }
+}
+
+var server = http.createServer(function(req, res) {
+ res.end("404: Not Found: " + request.url);
+});
+
+
+server.listen(cfg.fake_port);
+var wsdl_string = require('fs').readFileSync(path.resolve(cfg.wsdl_file), 'utf8');
+var soap_server = soap.listen(server, cfg.path, SoapServices, wsdl_string);
+soap_server.logger_req = function(xml, req, res) {
+ req.__time = moment().format('MMDD-HHmmss');
+ var cip = req.connection.remoteAddress;
+ var filename = 'logs/svr-' + req.__time + '-' + cip + '-req.log.xml';
+ var ws = fs.createWriteStream(filename);
+ ws.write(xml);
+ ws.end();
+};
+soap_server.logger_res = function(xml, req, res) {
+ var cip = req.connection.remoteAddress;
+ var filename = 'logs/svr-' + req.__time + '-' + cip + '-res.log.xml';
+ var ws = fs.createWriteStream(filename);
+ ws.write(xml);
+ ws.end();
+};
+
+var defLog = false;
+setTimeout(function() {
+ if (!defLog) return;
+ var def = soap_server.wsdl.definitions;
+ var message = def.messages[Object.keys(def.messages)[0]];
+ var service = def.services[Object.keys(def.services)[0]];
+ var binding = def.bindings[Object.keys(def.bindings)[0]];
+ var portType = def.portTypes[Object.keys(def.portTypes)[0]];
+ console.log(def);
+ console.log('\nmessage=');
+ console.log(message);
+ console.log('\nservice=');
+ console.log(service);
+ console.log('\nbinding=');
+ console.log(binding);
+ console.log('\nportType=');
+ console.log(portType);
+ console.log('\nbinding.method=');
+ console.log(binding.methods[Object.keys(binding.methods)[0]]);
+ console.log('\nportType.method=');
+ console.log(portType.methods[Object.keys(portType.methods)[0]]);
+},
+0);
+
+if (process.argv[2]) {
+ switch (process.argv[2].toLowerCase()) {
+ case 'deflog':
+ defLog = true;
+ break;
+ }
+} else {
+ console.log("Usage: node fake_server.js [deflog]");
+ console.log('[deflog] will log parsed wsdl definition parts;');
+ console.log();
+}
View
69 test/ip2tele/ip2tele.js
@@ -0,0 +1,69 @@
+var http = require('http');
+var soap = require('node-soap-ly');
+var moment = require('moment');
+var inspect = require('util').inspect;
+var url = require('url');
+var path = require('path');
+var ip2tele_client;
+var cfg = require('./cfg.js');
+
+if (process.argv[2] || process.argv[2] === 'fake') {
+ var endpoint = 'http://127.0.0.1:' + cfg.fake_port + cfg.path + '?wsdl';
+ var fake = 'fake';
+} else {
+ console.log('Usage : node ip2tele.js [fake]');
+ console.log('[fake] call fake service instead of the real production server.');
+}
+
+soap.createClient(cfg.wsdl_file,
+function(err, client) {
+ ip2tele_client = client;
+},
+endpoint
+);
+
+
+var web_server = http.createServer(function(req, res) {
+ var cSock = req.connection;
+ var sAddr = res.socket.address();
+ var QueryUserInfoRequest = {
+ 'UserInfo': {
+ "IP": cSock.remoteAddress,
+ "Port": cSock.remotePort,
+ 'ServerIP': sAddr.address,
+ 'ServerPort': sAddr.port,
+ 'SessionID': '',
+ 'SKey': ''
+ },
+ 'ServerInfo': {
+ 'ServerID': cfg.ServerID,
+ 'TimeStamp': moment().format('YYYYMMDDHHmmss')
+ }
+ }
+ console.log(QueryUserInfoRequest);
+
+ ip2tele_client.QueryUserInfoServiceApply.QueryUserInfoServiceApplyHttpPort.QueryUserInfoServiceApply(QueryUserInfoRequest,
+ function(err, QueryUserInfoResponse, body) {
+ if (err) console.log(err);
+ var path = url.parse(req.url).pathname;
+ console.log(QueryUserInfoResponse);
+ var tele = QueryUserInfoResponse.UserInfo[0].UserName;
+ var loc = 'http://61.181.22.71:81/tjuc/get_tele_b.show_tele?tele=' + tele;
+ if (path === '/') {
+ res.writeHead(200, {
+ 'Content-Type': 'text/html',
+ 'Transfer-Encoding': 'chunked'
+ });
+ res.write('<h2>This is a call to ' + (fake || 'real') + ' ip2tele web service</h2>');
+ res.write('<h1>your tele is ' + tele + '</h1>');
+ res.write('<br/>your request is <br/>' + inspect(QueryUserInfoRequest));
+ res.end('<br/>your response is <br/>' + inspect(QueryUserInfoResponse));
+ } else {
+ res.writeHead(303, {
+ "Location": loc
+ });
+ res.end();
+ }
+ });
+
+}).listen(cfg.site_port);
View
87 test/ip2tele/ip2tele.wsdl
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wsdl:definitions xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:tns="http://webservice.iuim.zoomtech.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope" targetNamespace="http://webservice.iuim.zoomtech.com/">
+ <wsdl:types>
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://webservice.iuim.zoomtech.com/">
+ <xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" name="QueryUserInfoRespone">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element minOccurs="0" name="ServerInfo">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="ResultCode" type="xs:string"/>
+ <xs:element name="Description" type="xs:string"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element minOccurs="0" name="UserInfo">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="UserName" type="xs:string"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" name="QueryUserInfoRequest">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="UserInfo">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="IP" type="xs:string"/>
+ <xs:element name="Port" type="xs:string"/>
+ <xs:element name="ServerIP" type="xs:string"/>
+ <xs:element name="ServerPort" type="xs:string"/>
+ <xs:element name="SessionID" type="xs:string"/>
+ <xs:element name="SKey" type="xs:string"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="ServerInfo">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="ServerID" type="xs:string"/>
+ <xs:element name="TimeStamp" type="xs:string"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xsd:schema>
+ </wsdl:types>
+
+ <wsdl:message name="QueryUserInfoServiceApplyResponse">
+ <wsdl:part name="QueryUserInfoRespone" element="tns:QueryUserInfoRespone"/>
+ </wsdl:message>
+ <wsdl:message name="QueryUserInfoServiceApplyRequest">
+ <wsdl:part name="QueryUserInfoRequest" element="tns:QueryUserInfoRequest"/>
+ </wsdl:message>
+
+ <wsdl:portType name="QueryUserInfoServiceApply">
+ <wsdl:operation name="QueryUserInfoServiceApply">
+ <wsdl:input name="QueryUserInfoServiceApplyRequest" message="tns:QueryUserInfoServiceApplyRequest"/>
+ <wsdl:output name="QueryUserInfoServiceApplyResponse" message="tns:QueryUserInfoServiceApplyResponse"/>
+ </wsdl:operation>
+ </wsdl:portType>
+
+ <wsdl:binding name="QueryUserInfoServiceApplyHttpBinding" type="tns:QueryUserInfoServiceApply">
+ <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
+ <wsdl:operation name="QueryUserInfoServiceApply">
+ <wsdlsoap:operation soapAction="http://webservice.iuim.zoomtech.com/QueryUserInfoServiceApply"/>
+ <wsdl:input name="QueryUserInfoServiceApplyRequest">
+ <wsdlsoap:body use="literal"/>
+ </wsdl:input>
+ <wsdl:output name="QueryUserInfoServiceApplyResponse">
+ <wsdlsoap:body use="literal"/>
+ </wsdl:output>
+ </wsdl:operation>
+ </wsdl:binding>
+
+ <wsdl:service name="QueryUserInfoServiceApply">
+ <wsdl:port name="QueryUserInfoServiceApplyHttpPort" binding="tns:QueryUserInfoServiceApplyHttpBinding">
+ <wsdlsoap:address location="http://localhost:8008/webservice_iuim/services/QueryUserInfoServiceApply"/>
+ </wsdl:port>
+ </wsdl:service>
+</wsdl:definitions>
View
0  test/ip2tele/logs/dummy
No changes.
View
22 test/ip2tele/readme.md
@@ -0,0 +1,22 @@
+Document Style web service request and response test
+----------
+
+Introduction of Business
+=====
+
+ The test suite establish a web server, when browser access this web server from cellular mobile network of Tianjin CHINA UNICOM, the web server will make request to a web service that can tell which mobile number is accessing the web server. So any mobile site can easily determine the client's mobile number.
+
+test remarks
+=====
+ The production ip2tele web service is located at 3rd-party which can not guarantee availability all the time, but I provide a fake\_server(fake\_server.js), you can start the fake server and call ip2tele with args to specify call fake server instead of the real server.
+
+ # for normal case to ask for production web-service at 3rd-party's
+ node ip2tele.js
+
+ # for closed test case to ask for fake web service withing this testing suite
+ node fake_server.js &
+ node ip2tele.js fake
+
+ When you use your mobile phone to access the web server, the response page will tell you your mobile number and all the json request/response text.
+
+ Since the WSDL use document style binding, I test the node\-soap module for document style. Notice that this soap module can allow single part message, server javascript will get only the internal contents of the top Element, but without the Element name. And client javascript should send internal contents of the top Element only, without TopElement:{internal content} form.
View
134 test/vac/EVacSyncService_SPClient.wsdl
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wsdl:definitions xmlns:tns3="http://rsp.spclient.evac.ericsson.com"
+ xmlns:impl="http://spclient.evac.ericsson.com"
+ xmlns:apachesoap="http://xml.apache.org/xml-soap"
+ xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:tns2="http://req.spclient.evac.ericsson.com"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+ targetNamespace="http://spclient.evac.ericsson.com">
+ <wsdl:types>
+ <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://req.spclient.evac.ericsson.com">
+ <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+ <complexType name="EOrderRelationUpdateNotifyReq">
+ <sequence>
+ <element name="RecordSequenceID" nillable="true" type="soapenc:string"/>
+ <element name="UserIdType" nillable="true" type="soapenc:int"/>
+ <element name="UserId" nillable="true" type="soapenc:string"/>
+ <element name="ServiceType" nillable="true" type="soapenc:string"/>
+ <element name="SpId" nillable="true" type="soapenc:string"/>
+ <element name="ProductId" nillable="true" type="soapenc:string"/>
+ <element name="UpdateType" nillable="true" type="soapenc:int"/>
+ <element name="UpdateTime" nillable="true" type="soapenc:string"/>
+ <element name="UpdateDesc" nillable="true" type="soapenc:string"/>
+ <element name="LinkID" nillable="true" type="soapenc:string"/>
+ <element name="Content" nillable="true" type="soapenc:string"/>
+ <element name="EffectiveDate" nillable="true" type="soapenc:string"/>
+ <element name="ExpireDate" nillable="true" type="soapenc:string"/>
+ <element name="Time_Stamp" nillable="true" type="soapenc:string"/>
+ <element name="EncodeStr" nillable="true" type="soapenc:string"/>
+ <element name="SubInfo" nillable="true" type="soapenc:string"/>
+ </sequence>
+ </complexType>
+
+ <complexType name="EMemOrderRelationUpdateNotifyReq">
+ <sequence>
+ <element name="RecordSequenceID" nillable="true" type="soapenc:string"/>
+ <element name="UserIdType" nillable="true" type="soapenc:int"/>
+ <element name="UserId" nillable="true" type="soapenc:string"/>
+ <element name="ServiceType" nillable="true" type="soapenc:string"/>
+ <element name="SpId" nillable="true" type="soapenc:string"/>
+ <element name="ProductId" nillable="true" type="soapenc:string"/>
+ <element name="UpdateType" nillable="true" type="soapenc:int"/>
+ <element name="UpdateTime" nillable="true" type="soapenc:string"/>
+ <element name="UpdateDesc" nillable="true" type="soapenc:string"/>
+ <element name="LinkID" nillable="true" type="soapenc:string"/>
+ <element name="Content" nillable="true" type="soapenc:string"/>
+ <element name="EffectiveDate" nillable="true" type="soapenc:string"/>
+ <element name="ExpireDate" nillable="true" type="soapenc:string"/>
+ <element name="Time_Stamp" nillable="true" type="soapenc:string"/>
+ <element name="EncodeStr" nillable="true" type="soapenc:string"/>
+ <element name="EUserIdType" nillable="true" type="soapenc:int"/>
+ <element name="EUserId" nillable="true" type="soapenc:string"/>
+ </sequence>
+ </complexType>
+ </schema>
+ <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://rsp.spclient.evac.ericsson.com">
+ <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
+ <complexType name="EOrderRelationUpdateNotifyRsp">
+ <sequence>
+ <element name="RecordSequenceID" nillable="true" type="soapenc:string"/>
+ <element name="ResultCode " type="xsd:int"/>
+ </sequence>
+ </complexType>
+
+ <complexType name="EMemOrderRelationUpdateNotifyRsp">
+ <sequence>
+ <element name="RecordSequenceID" nillable="true" type="soapenc:string"/>
+ <element name="ResultCode " type="xsd:int"/>
+ </sequence>
+ </complexType>
+ </schema>
+ </wsdl:types>
+
+
+ <wsdl:message name="eOrderRelationUpdateNotifyRequest">
+ <wsdl:part name="eOrderRelationUpdateNotifyRequest" type="tns2:EOrderRelationUpdateNotifyReq"/>
+ </wsdl:message>
+ <wsdl:message name="eOrderRelationUpdateNotifyResponse">
+ <wsdl:part name="eOrderRelationUpdateNotifyResponse" type="tns3:EOrderRelationUpdateNotifyRsp"/>
+ </wsdl:message>
+
+ <wsdl:message name="eMemOrderRelationUpdateNotifyRequest">
+ <wsdl:part name="eMemOrderRelationUpdateNotifyRequest" type="tns2:EMemOrderRelationUpdateNotifyReq"/>
+ </wsdl:message>
+ <wsdl:message name="eMemOrderRelationUpdateNotifyResponse">
+ <wsdl:part name="eMemOrderRelationUpdateNotifyResponse" type="tns3:EMemOrderRelationUpdateNotifyRsp"/>
+ </wsdl:message>
+
+
+ <wsdl:portType name="SyncNotifySPService">
+ <wsdl:operation name="eOrderRelationUpdateNotify" parameterOrder="eOrderRelationUpdateNotifyRequest">
+ <wsdl:input name="eOrderRelationUpdateNotifyRequest" message="impl:eOrderRelationUpdateNotifyRequest"/>
+ <wsdl:output name="eOrderRelationUpdateNotifyResponse" message="impl:eOrderRelationUpdateNotifyResponse"/>
+ </wsdl:operation>
+
+ <wsdl:operation name="eMemOrderRelationUpdateNotify" parameterOrder="eMemOrderRelationUpdateNotifyRequest">
+ <wsdl:input name="eMemOrderRelationUpdateNotifyRequest" message="impl:eMemOrderRelationUpdateNotifyRequest"/>
+ <wsdl:output name="eMemOrderRelationUpdateNotifyResponse" message="impl:eMemOrderRelationUpdateNotifyResponse"/>
+ </wsdl:operation>
+ </wsdl:portType>
+
+
+ <wsdl:binding name="SyncNotifySPSoapBinding" type="impl:SyncNotifySPService">
+ <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+
+ <wsdl:operation name="eOrderRelationUpdateNotify">
+ <wsdlsoap:operation/>
+ <wsdl:input>
+ <wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://spclient.evac.ericsson.com"/>
+ </wsdl:input>
+ <wsdl:output>
+ <wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://spclient.evac.ericsson.com"/>
+ </wsdl:output>
+ </wsdl:operation>
+
+ <wsdl:operation name="eMemOrderRelationUpdateNotify">
+ <wsdlsoap:operation/>
+ <wsdl:input>
+ <wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://spclient.evac.ericsson.com"/>
+ </wsdl:input>
+ <wsdl:output>
+ <wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://spclient.evac.ericsson.com"/>
+ </wsdl:output>
+ </wsdl:operation>
+ </wsdl:binding>
+
+
+ <wsdl:service name="ESyncNotifySPServiceService">
+ <wsdl:port name="ESyncNotifySP" binding="impl:SyncNotifySPSoapBinding">
+ <wsdlsoap:address location="http://localhost:8007/services/ESyncNotifySP"/>
+ </wsdl:port>
+ </wsdl:service>
+</wsdl:definitions>
View
28 test/vac/logs/vac_trace.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <soapenv:Body>
+ <ns1:eMemOrderRelationUpdateNotify xmlns:ns1="http://spclient.evac.ericsson.com" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <eMemOrderRelationUpdateNotifyRequest href="#id0"/>
+ </ns1:eMemOrderRelationUpdateNotify>
+ <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="http://req.spclient.evac.ericsson.com" id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns2:EMemOrderRelationUpdateNotifyReq">
+ <RecordSequenceID xsi:type="soapenc:string">1493804</RecordSequenceID>
+ <UserIdType href="#id1"/>
+ <UserId xsi:type="soapenc:string">860224922113</UserId>
+ <ServiceType xsi:type="soapenc:string">1</ServiceType>
+ <SpId xsi:type="soapenc:string">60123</SpId>
+ <ProductId xsi:type="soapenc:string">4212345681</ProductId>
+ <UpdateType href="#id1"/>
+ <UpdateTime xsi:type="soapenc:string">20120301092540</UpdateTime>
+ <UpdateDesc xsi:type="soapenc:string">1</UpdateDesc>
+ <LinkID xsi:type="soapenc:string"/>
+ <Content xsi:type="soapenc:string"/>
+ <EffectiveDate xsi:type="soapenc:string">20120228163657</EffectiveDate>
+ <ExpireDate xsi:type="soapenc:string">20500101000000</ExpireDate>
+ <Time_Stamp xsi:type="soapenc:string">0328125033</Time_Stamp>
+ <EncodeStr xsi:type="soapenc:string"/>
+ <EUserIdType href="#id1"/>
+ <EUserId xsi:type="soapenc:string">8615620001781</EUserId>
+ </multiRef>
+ <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id1" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:int">1</multiRef>
+ </soapenv:Body>
+</soapenv:Envelope>
View
250 test/vac/vac.js
@@ -0,0 +1,250 @@
+var cfg = {
+ wsdl_file: 'EVacSyncService_SPClient.wsdl',
+ service_url_path: '/services/ESyncNotifySP',
+ service_port: 8007,
+ psp_url: 'http://61.181.22.71:81/admin/if_vac_h'
+}
+var soap = require('node-soap-ly');
+var path = require('path');
+var http = require('http');
+var QueryString = require('querystring');
+var Request = require('request');
+var moment = require('moment');
+var fs = require('fs');
+
+var reqSeq = 0;
+var defLog = false;
+
+var SoapServices = {
+ 'ESyncNotifySPServiceService': {
+ 'ESyncNotifySP': {
+ 'eOrderRelationUpdateNotify': function(args) {
+ var fiber = Fiber.current;
+ var oraReqNV = args["eOrderRelationUpdateNotifyRequest"];
+ oraReqNV.SubInfo.split(',').forEach(function(line) {
+ if (!line) return;
+ var nv = line.split('=');
+ if (!nv[0]) return;
+ oraReqNV[nv[0]] = nv[1];
+ });
+ Request({
+ uri: cfg.psp_url + '.e',
+ method: 'post',
+ headers: {
+ 'Content-Type': "application/x-www-form-urlencoded"
+ },
+ body: QueryString.stringify(oraReqNV)
+ },
+ function(error, response, body) {
+ if (!error && response.statusCode === 200) {
+ fiber.run({
+ 'eOrderRelationUpdateNotifyResponse': {
+ 'RecordSequenceID': oraReqNV.RecordSequenceID,
+ 'ResultCode': body
+ }
+ });
+ } else {
+ fiber.run({
+ 'eOrderRelationUpdateNotifyResponse': {
+ 'RecordSequenceID': oraReqNV.RecordSequenceID,
+ 'ResultCode': 1
+ }
+ });
+ }
+ });
+ return yield();
+ },
+ 'eMemOrderRelationUpdateNotify': function(args) {
+ var fiber = Fiber.current;
+ args = args["eMemOrderRelationUpdateNotifyRequest"];
+ var oraReqNV = {
+ 'billnum': args.EUserId,
+ 'UpdateType': args.UpdateType,
+ 'ProductId': args.ProductId,
+ 'stime': args.EffectiveDate,
+ 'etime': args.ExpireDate,
+ 'mobile': args.UserId.substr( - 11)
+ };
+ Request({
+ uri: cfg.psp_url + '.m',
+ method: 'post',
+ headers: {
+ 'Content-Type': "application/x-www-form-urlencoded"
+ },
+ body: QueryString.stringify(oraReqNV)
+ },
+ function(error, response, body) {
+ if (!error && response.statusCode === 200) {
+ fiber.run({
+ 'eMemOrderRelationUpdateNotifyResponse': {
+ 'RecordSequenceID': args.RecordSequenceID,
+ 'ResultCode': body
+ }
+ });
+ } else {
+ fiber.run({
+ 'eMemOrderRelationUpdateNotifyResponse': {
+ 'RecordSequenceID': args.RecordSequenceID,
+ 'ResultCode': 1
+ }
+ });
+ }
+ });
+ return yield();
+ }
+ }
+ }
+}
+
+var server = http.createServer(function(req, res) {
+ res.end("404: Not Found: " + request.url);
+});
+server.listen(cfg.service_port);
+var wsdl_string = require('fs').readFileSync(path.resolve(cfg.wsdl_file), 'utf8');
+var soap_server = soap.listen(server, cfg.service_url_path, SoapServices, wsdl_string);
+
+soap_server.logger_req = function(xml, req, res) {
+ req.__time = moment().format('MMDD-HHmmss');
+ var cip = req.connection.remoteAddress;
+ var filename = 'logs/srv-' + req.__time + '-' + (++reqSeq) + '-' + cip + '-req.log.xml';
+ var ws = fs.createWriteStream(filename);
+ ws.write(xml);
+ ws.end();
+};
+soap_server.logger_res = function(xml, req, res) {
+ var cip = req.connection.remoteAddress;
+ var filename = 'logs/srv-' + req.__time + '-' + '-' + (++reqSeq) + '-' + cip + '-res.log.xml';
+ var ws = fs.createWriteStream(filename);
+ ws.write(xml);
+ ws.end();
+};
+
+setTimeout(function() {
+ if (!defLog) return;
+ var def = soap_server.wsdl.definitions;
+ var schema = def.schemas[Object.keys(def.schemas)[0]];
+ var service = def.services[Object.keys(def.services)[0]];
+ var binding = def.bindings[Object.keys(def.bindings)[0]];
+ var portType = def.portTypes[Object.keys(def.portTypes)[0]];
+ var binding_method = binding.methods[Object.keys(binding.methods)[0]];
+ var portType_method = portType.methods[Object.keys(portType.methods)[0]];
+ var input = portType_method.input;
+ var type = schema.complexTypes[Object.keys(schema.complexTypes)[0]];
+ console.log(def);
+ console.log('\nschema=');
+ console.log(schema);
+ console.log('\nservice=');
+ console.log(service);
+ console.log('\nbinding=');
+ console.log(binding);
+ console.log('\nportType=');
+ console.log(portType);
+ console.log('\nbinding.method=');
+ console.log(binding_method);
+ console.log('\nportType.method=');
+ console.log(portType_method);
+ console.log('---');
+ console.log(portType_method.description(def));
+ console.log('\nportType.method.input=');
+ console.log(input);
+ console.log('\nportType.method.input.parts=');
+ console.log(input.parts);
+},
+0);
+
+var logSeq = 0;
+function do_test(client) {
+ client.logger_req = function(xml) {
+ var time = moment().format('MMDD-HHmmss');
+ var filename = 'logs/cli-' + time + '-' + (++logSeq) + '-req.log.xml';
+ var ws = fs.createWriteStream(filename);
+ ws.write(xml);
+ ws.end();
+ };
+ client.logger_res = function(xml) {
+ var time = moment().format('MMDD-HHmmss');
+ var filename = 'logs/cli-' + time + '-' + '-' + (++logSeq) + '-res.log.xml';
+ var ws = fs.createWriteStream(filename);
+ ws.write(xml);
+ ws.end();
+ };
+ client.ESyncNotifySPServiceService.ESyncNotifySP.eOrderRelationUpdateNotify({
+ "eOrderRelationUpdateNotifyRequest": {
+ "RecordSequenceID": 12345678901,
+ "UserIdType": 1,
+ "UserId": "8612312345678",
+ "ServiceType": "ServiceType",
+ "SpId": "SpId",
+ "ProductId": "ProductId",
+ "UpdateType": 1,
+ "UpdateTime": "20120228163657",
+ "UpdateDesc": "UpdateDesc",
+ "LinkID": "LinkID",
+ "Content": "Content",
+ "EffectiveDate": "20120228163657",
+ "ExpireDate": "20120228163657",
+ "Time_Stamp": "20120228163657",
+ "EncodeStr": "EncodeStr",
+ "SubInfo": "gid=tjuc"
+ }
+ },
+ function(err, result, body) {
+ if (err) {
+ console.log(err);
+ return;
+ }
+ console.log(result);
+ });
+ client.ESyncNotifySPServiceService.ESyncNotifySP.eMemOrderRelationUpdateNotify({
+ "eMemOrderRelationUpdateNotifyRequest": {
+ "RecordSequenceID": 12345678902,
+ "UserIdType": 1,
+ "UserId": "15620009233",
+ "ServiceType": "ServiceType",
+ "SpId": "SpId",
+ "ProductId": "ProductId",
+ "UpdateType": 1,
+ "UpdateTime": "UpdateTime",
+ "UpdateDesc": "UpdateDesc",
+ "LinkID": "LinkID",
+ "Content": "Content",
+ "EffectiveDate": "20120228163657",
+ "ExpireDate": "20120228163657",
+ "Time_Stamp": "20120228163657",
+ "EncodeStr": "EncodeStr",
+ "EUserIdType": 1,
+ "EUserId": "8612312345678"
+ }
+ },
+ function(err, result) {
+ if (err) {
+ console.log(err);
+ return;
+ }
+ console.log(result);
+ });
+}
+
+
+if (process.argv[2]) {
+ switch (process.argv[2].toLowerCase()) {
+ case 'log':
+ defLog = true;
+ break;
+ case 'test':
+ soap.createClient('http://127.0.0.1:' + cfg.service_port + cfg.service_url_path + '?wsdl',
+ function(err, client) {
+ setTimeout(function(){
+ console.log('-- in client ' + Object.keys(client.wsdl));
+ do_test(client);
+ },3);
+ });
+ }
+} else {
+ console.log("Usage: node vac.js [test|log]");
+ console.log('test will make test requests;');
+ console.log('log will log parsed wsdl definition parts;');
+ console.log();
+}
+
+
Please sign in to comment.
Something went wrong with that request. Please try again.