Gathering detailed insights and metrics for strong-soap
Gathering detailed insights and metrics for strong-soap
Gathering detailed insights and metrics for strong-soap
Gathering detailed insights and metrics for strong-soap
SOAP driver for Node.js (A complete rewrite of node-soap)
npm install strong-soap
Typescript
Module System
Min. Node Version
Node Version
NPM Version
62.3
Supply Chain
92.8
Quality
90.5
Maintenance
100
Vulnerability
95.6
License
JavaScript (99.98%)
Shell (0.02%)
Total Downloads
6,020,237
Last Day
1,883
Last Week
24,481
Last Month
116,998
Last Year
1,273,454
394 Stars
1,617 Commits
164 Forks
37 Watching
35 Branches
181 Contributors
Minified
Minified + Gzipped
Latest Version
4.1.7
Package Id
strong-soap@4.1.7
Unpacked Size
1.15 MB
Size
220.07 kB
File Count
295
NPM Version
10.5.1
Node Version
18.18.2
Publised On
04 Dec 2024
Cumulative downloads
Total Downloads
Last day
-60.1%
1,883
Compared to previous day
Last week
-22.4%
24,481
Compared to previous week
Last month
0.8%
116,998
Compared to previous month
Last year
41.2%
1,273,454
Compared to previous year
12
This module provides a Node.js SOAP client for invoking web services and a mock-up SOAP server capability to create and test your web service. This module is based on node-soap
module.
Features:
Node.js version 10, 12, and 14 are officially supported. We dropped version 8 support in 3.0.0.
Install with npm:
1npm install strong-soap
Start with the WSDL for the web service you want to invoke. For example, the stock quote service http://www.webservicex.net/stockquote.asmx and the WSDL is http://www.webservicex.net/stockquote.asmx?WSDL
Create a new SOAP client from WSDL URL using soap.createClient(url[, options], callback)
. Also supports a local file system path. An instance of Client
is passed to the soap.createClient
callback. It is used to execute methods on the soap service.
1"use strict"; 2 3var soap = require('strong-soap').soap; 4// wsdl of the web service this client is going to invoke. For local wsdl you can use, url = './wsdls/stockquote.wsdl' 5var url = 'http://www.webservicex.net/stockquote.asmx?WSDL'; 6 7var requestArgs = { 8 symbol: 'IBM' 9}; 10 11var options = {}; 12soap.createClient(url, options, function(err, client) { 13 var method = client['StockQuote']['StockQuoteSoap']['GetQuote']; 14 method(requestArgs, function(err, result, envelope, soapHeader) { 15 //response envelope 16 console.log('Response Envelope: \n' + envelope); 17 //'result' is the response body 18 console.log('Result: \n' + JSON.stringify(result)); 19 }); 20}); 21
As well as creating a client via a url
, an existing WSDL object can be passed in via options.WSDL_CACHE
.
1var soap = require('strong-soap').soap; 2var WSDL = soap.WSDL; 3 4var url = 'http://www.webservicex.net/stockquote.asmx?WSDL'; 5 6// Pass in WSDL options if any 7 8var options = {}; 9WSDL.open(url,options, 10 function(err, wsdl) { 11 // You should be able to get to any information of this WSDL from this object. Traverse 12 // the WSDL tree to get bindings, operations, services, portTypes, messages, 13 // parts, and XSD elements/Attributes. 14 15 // Set the wsdl object in the cache. The key (e.g. 'stockquotewsdl') 16 // can be anything, but needs to match the parameter passed into soap.createClient() 17 var clientOptions = { 18 WSDL_CACHE : { 19 stockquotewsdl: wsdl 20 } 21 }; 22 soap.createClient('stockquotewsdl', clientOptions, function(err, client) { 23 var method = client['StockQuote']['StockQuoteSoap']['GetQuote']; 24 method(requestArgs, function(err, result, envelope, soapHeader) { 25 26 //response envelope 27 console.log('Response Envelope: \n' + envelope); 28 //'result' is the response body 29 console.log('Result: \n' + JSON.stringify(result)); 30 }); 31 }); 32});
The Request envelope created by above service invocation:
1<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 2<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> 3 <soap:Header/> 4 <soap:Body> 5 <ns1:GetQuote xmlns:ns1="http://www.webserviceX.NET/"> 6 <ns1:symbol>IBM</ns1:symbol> 7 </ns1:GetQuote> 8 </soap:Body> 9</soap:Envelope>
This WSDL operation is defined as document/literal-wrapped style. Hence the request in soap is wrapped in operation name. Refer to test cases server-client-document-test and server-client-rpc-test to understand document and rpc styles and their Request, Response and Fault samples.
The options
argument allows you to customize the client with the following properties:
endpoint
: to override the SOAP service's host specified in the .wsdl
file.request
: to override the request module.httpClient
: to provide your own http client that implements request(rurl, data, callback, exheaders, exoptions)
.envelopeKey
: to set specific key instead of <soap:Body></soap:Body>
wsdl_options
: custom options for the request module on WSDL requests.wsdl_headers
: custom HTTP headers to be sent on WSDL requests.Note: for versions of node >0.10.X, you may need to specify {connection: 'keep-alive'}
in SOAP headers to avoid truncation of longer chunked responses.
User can define extra HTTP headers to be sent on the request.
1var clientOptions = {}; 2soap.createClient(url, clientOptions, function(err, client) { 3 var customRequestHeader = {customheader1: 'test1'}; 4 // Custom request header 5 client.GetQuote(requestArgs, function(err, result, envelope) { 6 // Result in SOAP envelope body which is the wrapper element. 7 // In this case, result object corresponds to GetCityForecastByZIPResponse. 8 console.log(JSON.stringify(result)); 9 }, null, customRequestHeader); 10});
Describes services, ports and methods as a JavaScript object.
1// Describes the entire WSDL in a JSON tree object form. 2var description = client.describe(); 3// Inspect GetQuote operation. You can inspect Service: {Port: {operation: { 4console.log(JSON.stringify(description.StockQuote.StockQuoteSoap.GetQuote));
Use the specified security protocol.
Refer to test case ssl-test for an example of using this API.
Call method on the SOAP service.
1 client.MyFunction({name: 'value'}, function(err, result, envelope, soapHeader) {
2 // Result is a javascript object
3 // Envelope is the response envelope from the Web Service
4 // soapHeader is the response soap header as a JavaScript object
5 })
A method can also be called as a promise.
1 client.MyFunction({name: 'value'}).then(function({result, envelope, soapHeader}){ 2 // ... 3 }, function(err) { 4 // ... 5 }); 6 7 // in async/await flavor 8 try { 9 const {result, envelope, soapHeader} = await client.MyFunction({name: 'value'}); 10 } catch(err) { 11 // handle error 12 }
Call a method using a specific service and port.
1 client.MyService.MyPort.MyFunction({name: 'value'}, function(err, result) {
2 // Result is a JavaScript object
3 })
Accepts any option that the request module accepts, see request module.
For example, you could set a timeout of 5 seconds on the request like this:
1 client.MyService.MyPort.MyFunction({name: 'value'}, function(err, result) {
2 // result is a javascript object
3 }, {timeout: 5000})
You can measure the elapsed time on the request by passing the time option:
1 client.MyService.MyPort.MyFunction({name: 'value'}, function(err, result) {
2 // client.lastElapsedTime - the elapsed time of the last request in milliseconds
3 }, {time: true})
To align method call signature with Node's standard callback-last pattern and eventually allow promisification of method calls, the following method signatures are also supported:
1client.MyService.MyPort.MyFunction({name: 'value'}, options, function (err, result) {
2 // result is a javascript object
3})
4
5client.MyService.MyPort.MyFunction({name: 'value'}, options, extraHeaders, function (err, result) {
6 // result is a javascript object
7})
The property that contains last full soap request for client logging.
Overwrites the SOAP service endpoint address.
Client instances emit the following events:
IncomingMessage
response object.
This is emitted for all responses (both success and errors).For an example of using this API, see ssl-test.
Here is an example of 'soapError' event
1soap.createClient(__dirname + '/wsdl/default_namespace.wsdl', function (err, client) {
2 var didEmitEvent = false;
3 client.on('soapError', function(err) {
4 didEmitEvent = true;
5 assert.ok(err.root.Envelope.Body.Fault);
6 });
7 client.MyOperation({}, function(err, result) {
8 assert.ok(didEmitEvent);
9 done();
10 });
11}, baseUrl);
strong-soap
has several default security protocols. You can easily add your own
as well. The interface is quite simple. Each protocol defines two methods:
addOptions
- Method that accepts an options arg that is eventually passed directly to request
toXML
- Method that returns a string of XML.1 client.setSecurity(new soap.BasicAuthSecurity('username', 'password'));
1 client.setSecurity(new soap.BearerSecurity('token'));
Note: If you run into issues using this protocol, consider passing these options as default request options to the constructor:
rejectUnauthorized: false
strictSSL: false
secureOptions: constants.SSL_OP_NO_TLSv1_2
(this is likely needed for node >= 10.0)1 client.setSecurity(new soap.ClientSSLSecurity(
2 '/path/to/key'
3 , '/path/to/cert'
4 , {/*default request options*/}
5 ));
WSSecurity
implements WS-Security. UsernameToken and PasswordText/PasswordDigest is supported.
1 var wsSecurity = new WSSecurity(username, password, options) 2 //the 'options' object is optional and contains properties: 3 //passwordType: 'PasswordDigest' or 'PasswordText' default is PasswordText 4 //hasTimeStamp: true or false, default is true 5 //hasTokenCreated: true or false, default is true 6 client.setSecurity(wsSecurity);
WS-Security X509 Certificate support.
1 var privateKey = fs.readFileSync(privateKeyPath); 2 var publicKey = fs.readFileSync(publicKeyPath); 3 var password = ''; // optional password 4 var wsSecurity = new soap.WSSecurityCert(privateKey, publicKey, password, 'utf8'); 5 client.setSecurity(wsSecurity);
Note: Optional dependency 'ursa' is required to be installed successfully when WSSecurityCert is used.
1 const pfxSecurity = new soap.ClientSSLSecurityPFX(pathToPfxOrFileBuffer, passphrase)
2 client.setSecurity(pfxSecurity)
To override the default behavior of strong-soap
, use the wsdlOptions
object, passed in the
createClient()
method. The wsdlOptions
has the following properties:
1var wsdlOptions = { 2 attributesKey: 'theAttrs', 3 valueKey: 'theVal', 4 xmlKey: 'theXml' 5}
If you call createClient()
with no options (or an empty Object {}
), strong-soap
defaults
to the following:
attributesKey
: '$attributes'
valueKey
: '$value'
xmlKey
: '$xml'
By default, strong-soap
uses $value
as key for any parsed XML value which may interfere with your other code as it
could be some reserved word, or the $
in general cannot be used for a key to start with.
You can define your own valueKey
by passing it in the wsdl_options
to the createClient call like so:
1var wsdlOptions = {
2 valueKey: 'theVal'
3};
4
5soap.createClient(__dirname + '/wsdl/default_namespace.wsdl', wsdlOptions, function (err, client) {
6 // your code
7});
As valueKey
, strong-soap
uses $xml
as key. The xml key is used to pass XML Object without adding namespace or parsing the string.
Example :
1dom = { 2 $xml: '<parentnode type="type"><childnode></childnode></parentnode>' 3};
1<tns:dom> 2 <parentnode type="type"> 3 <childnode></childnode> 4 </parentnode> 5</tns:dom>
You can define your own xmlKey
by passing it in the wsdl_options
to the createClient call like this:
1var wsdlOptions = {
2 xmlKey: 'theXml'
3};
4
5soap.createClient(__dirname + '/wsdl/default_namespace.wsdl', wsdlOptions, function (err, client) {
6 // your code
7});
You can achieve attributes like:
1<parentnode> 2 <childnode name="childsname"> 3 </childnode> 4</parentnode>
By attaching an attributes object to a node.
1{ 2 parentnode: { 3 childnode: { 4 $attributes: { 5 name: 'childsname' 6 } 7 } 8 } 9} 10
However, "attributes" may be a reserved key for some systems that actually want a node:
1<attributes> 2</attributes>
In this case you can configure the attributes key in the wsdlOptions
like this:
1var wsdlOptions = { 2 attributesKey: '$attributes' 3};
Adding xsiType
1soap.createClient(__dirname + '/wsdl/default_namespace.wsdl', wsdlOptions, function (err, client) {
2 client.*method*({
3 parentnode: {
4 childnode: {
5 $attributes: {
6 $xsiType: "{xmlnsTy}Ty"
7 }
8 }
9 }
10 });
11});
Removing the xsiType. The resulting Request shouldn't have the attribute xsiType
1soap.createClient(__dirname + '/wsdl/default_namespace.wsdl', wsdlOptions, function (err, client) {
2 client.*method*({
3 parentnode: {
4 childnode: {
5 $attributes: {
6
7 }
8 }
9 }
10 });
11});
To see it in practice, consider the sample in: test/request-response-samples/addPets__force_namespaces
XMLHandler enables you to to convert a JSON object to XML and XML to a JSON object. It can also parse an XML string or stream into the XMLBuilder tree.
API to convert JSON object to XML and XML to JSON object:
1var soap = require('..').soap; 2var XMLHandler = soap.XMLHandler; 3var xmlHandler = new XMLHandler(); 4var util = require('util'); 5var url = 'http://www.webservicex.net/stockquote.asmx?WSDL'; 6 7var requestArgs = { 8 symbol: 'IBM' 9}; 10 11var options = {}; 12var clientOptions = {}; 13soap.createClient(url, clientOptions, function(err, client) { 14 var customRequestHeader = {customheader1: 'test1'}; 15 client.GetQuote(requestArgs, function(err, result, envelope, soapHeader) { 16 // Convert 'result' JSON object to XML 17 var node = xmlHandler.jsonToXml(null, null, 18 XMLHandler.createSOAPEnvelopeDescriptor('soap'), result); 19 var xml = node.end({pretty: true}); 20 console.log(xml); 21 22 // Convert XML to JSON object 23 var root = xmlHandler.xmlToJson(null, xml, null); 24 console.log('%s', util.inspect(root, {depth: null})); 25 26 }, options, customRequestHeader); 27});
Parse XML string or stream into the XMLBuilder tree:
1var root = XMLHandler.parseXml(null, xmlString);
Loads WSDL into a tree form. Traverse through WSDL tree to get to bindings, services, ports, operations, and so on.
Parameters:
wsdlURL
WSDL url to load.options
WSDL optionscallback
Error and WSDL loaded into object tree.1var soap = require('..').soap; 2var WSDL = soap.WSDL; 3var path = require('path'); 4 5// Pass in WSDL options if any 6 7var options = {}; 8WSDL.open('./wsdls/stockquote.wsdl',options, 9 function(err, wsdl) { 10 // You should be able to get to any information of this WSDL from this object. Traverse 11 // the WSDL tree to get bindings, operations, services, portTypes, messages, 12 // parts, and XSD elements/Attributes. 13 14 var getQuoteOp = wsdl.definitions.bindings.StockQuoteSoap.operations.GetQuote; 15 // print operation name 16 console.log(getQuoteOp.$name); 17 var service = wsdl.definitions.services['StockQuote']; 18 //print service name 19 console.log(service.$name); 20});
Loads WSDL into a tree form directly from memory. It traverses through WSDL tree to get to bindings, services, ports, operations, and so on as long as you have your dependent WSDLs and schemas are loaded and available in the options.WSDL_CACHE
. If any I/O is required to retrieve any dependencies this call will throw an error.
Parameters:
wsdlURL
WSDL url to load as named in the cache.options
WSDL optionsAn example of loading WSDLs into your options.WSDL_CACHE
and calling wsdl.loadSync()
can be found in the test test/wsdl-load-from-memory-test
Creates a new SOAP server that listens on path and provides services.
wsdl is an xml string that defines the service.
1 var myService = { 2 MyService: { 3 MyPort: { 4 MyFunction: function(args) { 5 return { 6 name: args.name 7 }; 8 }, 9 10 // This is how to define an asynchronous function. 11 MyAsyncFunction: function(args, callback) { 12 // do some work 13 callback({ 14 name: args.name 15 }); 16 }, 17 18 // This is how to receive incoming headers 19 HeadersAwareFunction: function(args, cb, headers) { 20 return { 21 name: headers.Token 22 }; 23 }, 24 25 // You can also inspect the original `req` 26 reallyDetailedFunction: function(args, cb, headers, req) { 27 console.log('SOAP `reallyDetailedFunction` request from ' + req.connection.remoteAddress); 28 return { 29 name: headers.Token 30 }; 31 } 32 } 33 } 34 }; 35 36 var xml = require('fs').readFileSync('myservice.wsdl', 'utf8'), 37 server = http.createServer(function(request,response) { 38 response.end("404: Not Found: " + request.url); 39 }); 40 41 server.listen(8000); 42 soap.listen(server, '/wsdl', myService, xml);
An example of using the SOAP server is in test/server-client-document-test
You can pass in server and WSDL Options using an options hash.
1var xml = require('fs').readFileSync('myservice.wsdl', 'utf8'); 2 3soap.listen(server, { 4 // Server options. 5 path: '/wsdl', 6 services: myService, 7 xml: xml, 8 9 // WSDL options. 10 attributesKey: 'theAttrs', 11 valueKey: 'theVal', 12 xmlKey: 'theXml' 13});
If the log
method is defined it will be called with 'received' and 'replied'
along with data.
1 server = soap.listen(...) 2 server.log = function(type, data) { 3 // type is 'received' or 'replied' 4 };
Server instances emit the following events:
function(request, methodName)
.function(headers, methodName)
.The sequence order of the calls is request
, headers
and then the dedicated
service method.
1 test.soapServer.on('request', function requestManager(request, methodName) { 2 assert.equal(methodName, 'GetLastTradePrice'); 3 done(); 4 }); 5
An example of using the SOAP server is in test/server-test
A service method can reply with a SOAP Fault to a client by throw
ing an
object with a Fault
property.
Example SOAP 1.1 Fault:
1 test.service = { 2 DocLiteralWrappedService: { 3 DocLiteralWrappedPort: { 4 myMethod: function (args, cb, soapHeader) { 5 throw { 6 Fault: { 7 faultcode: "sampleFaultCode", 8 faultstring: "sampleFaultString", 9 detail: 10 { myMethodFault: 11 {errorMessage: 'MyMethod Business Exception message', value: 10} 12 } 13 } 14 } 15 } 16 } 17 } 18 }
SOAP 1.2 Fault:
1 test.service = { 2 DocLiteralWrappedService: { 3 DocLiteralWrappedPort: { 4 myMethod: function (args, cb, soapHeader) { 5 throw { 6 Fault: { 7 Code: { 8 Value: "soap:Sender", 9 Subcode: { Value: "rpc:BadArguments" } 10 }, 11 Reason: { Text: "Processing Error" }, 12 Detail: 13 {myMethodFault2: 14 {errorMessage2: 'MyMethod Business Exception message', value2: 10} 15 } 16 } 17 } 18 } 19 } 20 } 21 }
Examples of SOAP 1.1/SOAP 1.2 Fault response can be found in test test/server-client-document-test
If server.authenticate
is not defined then no authentication will take place.
1 server = soap.listen(...) 2 server.authenticate = function(security) { 3 var created, nonce, password, user, token; 4 token = security.UsernameToken, user = token.Username, 5 password = token.Password, nonce = token.Nonce, created = token.Created; 6 return user === 'user' && password === soap.passwordDigest(nonce, created, 'password'); 7 };
The server.authorizeConnection
method is called prior to the soap service method.
If the method is defined and returns false
then the incoming connection is
terminated.
1 server = soap.listen(...) 2 server.authorizeConnection = function(req) { 3 return true; // or false 4 };
A service method can look at the SOAP headers by providing a third arguments.
1 { 2 HeadersAwareFunction: function(args, cb, headers) { 3 return { 4 name: headers.Token 5 }; 6 } 7 }
It is also possible to subscribe to the 'headers' event. The event is triggered before the service method is called, and only when the SOAP Headers are not empty.
1 server = soap.listen(...) 2 server.on('headers', function(headers, methodName) { 3 // It is possible to change the value of the headers 4 // before they are handed to the service method. 5 // It is also possible to throw a SOAP Fault 6 });
First parameter is the Headers object; second parameter is the name of the SOAP method that will called (in case you need to handle the headers differently based on the method).
Both client and server can define SOAP headers that will be added to what they send. They provide the following methods to manage the headers.
Adds soapHeader to soap:Header node.
Parameters:
value
JSON object representing {headerName: headerValue} or XML string.qname
qname used for the headeraddSoapHeader(value, qname, options);
Returns the index where the header is inserted.
Changes an existing soapHeader.
Parameters:
index
index of the header to replace with provided new valuevalue
JSON object representing {headerName: headerValue} or XML string.qname
qname used for the headerReturns all defined headers.
Removes all defined headers.
Examples of using SOAP header API are in: test/server-test and test/server-test
Unit testing services that use SOAP clients can be very cumbersome. To get
around this you can use soap-stub
in conjunction with sinon
to stub soap with
your clients.
1var sinon = require('sinon'); 2var soapStub = require('strong-soap/soap-stub'); 3 4var urlMyApplicationWillUseWithCreateClient = './example/stockquote.wsdl'; 5var clientStub = { 6 SomeOperation: sinon.stub() 7}; 8 9clientStub.SomeOperation.respondWithError = soapStub.createRespondingStub({error: 'error'}); 10clientStub.SomeOperation.respondWithSuccess = soapStub.createRespondingStub({success: 'success'}); 11// or if you are using promises 12clientStub.SomeOperation.respondWithError = soapStub.createRespondingStubAsync({error: 'error'}); 13clientStub.SomeOperation.respondWithSuccess = soapStub.createRespondingStubAsync({success: 'success'}); 14 15 16soapStub.registerClient('my client alias', urlMyApplicationWillUseWithCreateClient, clientStub); 17 18 19var fs = require('fs'), 20 assert = require('assert'), 21 request = require('@cypress/request'), 22 http = require('http'), 23 lastReqAddress; 24 25describe('myService', function() { 26 var clientStub; 27 var myService; 28 29 beforeEach(function() { 30 clientStub = soapStub.getStub('my client alias'); 31 soapStub.reset(); 32 myService = clientStub; 33 }); 34 35 describe('failures', function() { 36 beforeEach(function() { 37 clientStub.SomeOperation.respondWithError(); 38 }); 39 40 it('should handle error responses', function() { 41 myService.SomeOperation(function(err, response) { 42 // handle the error response. 43 }); 44 }); 45 }); 46}); 47
No vulnerabilities found.
No security vulnerabilities found.