├── README.md ├── gulpfile.js ├── karma.conf.js ├── package.json ├── src ├── karma.@angular.shim.js ├── package.json ├── soap.service.ts ├── spec.ts └── systemjs.config.js ├── tsconfig.json ├── typings.d.ts └── typings.json /README.md: -------------------------------------------------------------------------------- 1 | # Angular 2 SOAP client service 2 | ## Description 3 | `autopulous-angular2-soap` is a module to make SOAP requests and receive SOAP responses as a JavaScript object (JSO). 4 | ## License 5 | The MIT License (MIT) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | ## API 13 | ### `constructor(servicePort, serviceRoot, targetNamespace)` 14 | SoapService constructor - invoked via Angular 2 `provider`, or JavaScript `new` 15 | #### `servicePort` 16 | argument `string` 17 | 18 | the protocol, resource name, and port upon which the service endpoint is deployed 19 | #### `serviceRoot` 20 | argument `string` 21 | 22 | the path to the webservice endpoint 23 | #### `targetNamespace` 24 | optional argument `string` 25 | 26 | prefixes the webservice method in the HTTP request header `SOAPAction` 27 | ### `envelopeBuilder` 28 | property **`(requestBody:string) => string`** 29 | 30 | a callback function to build the SOAP request envelope 31 | ### `jsoResponseHandler` 32 | property `(response:{}) => void` 33 | 34 | a callback function to receive the JavaScript webservice response object (https://github.com/autopulous/xdom2jso.git) 35 | ### `xmlResponseHandler` 36 | property `(response:NodeListOf) => void` 37 | 38 | a callback function to asynchronously consume the XML webservice response 39 | ### `localNameMode` 40 | property `boolean` 41 | 42 | when `true` causes the XML webservice response conversion to omit `ELEMENT` and `ATTRIBUTE` name namespaces within the JavaScript object 43 | ### `debugMode` 44 | property `boolean` 45 | 46 | when `true` causes webservice request/response debug messages to be sent to the console 47 | ### `testMode` 48 | property `boolean` 49 | 50 | when `true` causes the HTTP request to be processed synchronously and sets `debugMode` to true (used for unit testing or diagnostics) 51 | 52 | ### `post(method, parameters, responseRoot)` 53 | method returning `void` 54 | 55 | #### `method` 56 | argument `string` 57 | 58 | HTTP request header `SOAPAction` method 59 | #### `parameters` 60 | argument `{}` (JavaScript object) 61 | 62 | the body of the webservice request 63 | #### `responseRoot` 64 | optional argument `string` 65 | 66 | the starting `ELEMENT` within the XML webservice response from which to convert into the JavaScript response object 67 | ## Usage Notes 68 | * By default `autopulous-angular2-soap` processes SOAP responses asynchronously (this behavior is overridden by setting `testMode`) 69 | 70 | ## Example 71 | A user authentication service to make SOAP requests and process SOAP responses. 72 | 73 | `user.authenticator.ts` 74 | 75 | import {Component} from "@angular/core"; 76 | import {SoapService} from "../soap.service"; 77 | 78 | @Component({ 79 | selector: 'authenticator', 80 | templateUrl: 'app/authenticator.component.html', 81 | providers: [SoapService] 82 | }) 83 | 84 | export class UserAuthentication { 85 | private servicePort:string = 'http://172.16.62.111:8091'; 86 | private servicePath:string = '/your-application-ws/ws/'; 87 | private targetNamespace:string = ''; 88 | 89 | private responseJso:{} = null; 90 | 91 | private soapService:SoapService; 92 | 93 | constructor() { 94 | this.soapService = new SoapService(this.servicePort, this.servicePath, this.targetNamespace); 95 | this.soapService.envelopeBuilder = this.envelopeBuilder; 96 | this.soapService.jsoResponseHandler = (response:{}) => {this.responseJso = response}; 97 | this.soapService.localNameMode = true; 98 | } 99 | 100 | private username:string = ''; 101 | private password:string = ''; 102 | 103 | public login(username:string, password:string) { 104 | var method:string = 'Login'; 105 | var parameters:{}[] = []; 106 | 107 | this.username = username; 108 | this.password = password; 109 | 110 | parameters['LoginRequest xmlns="urn:application:security:messages:1:0"'] = UserAuthentication.userLogin(username, password); 111 | 112 | this.soapService.post(method, parameters); 113 | } 114 | 115 | private static userLogin(username, password):{}[] { 116 | var parameters:{}[] = []; 117 | 118 | parameters["UserName"] = username; 119 | parameters['Password'] = password; 120 | 121 | return parameters; 122 | } 123 | 124 | private envelopeBuilder(requestBody:string):string { 125 | return "" + 126 | "" + 127 | "" + 128 | "" + 129 | "" + this.username + "" + 130 | "" + this.password + "" + 131 | "" + 132 | "" + 133 | "" + 134 | "" + 135 | requestBody + 136 | "" + 137 | ""; 138 | } 139 | } 140 | 141 | `index.html` 142 | 143 | Load module dependencies `autopulous-xdom` and `autopulous-xdom2jso` 144 | 145 | 146 | 147 | 148 | ... 149 | 150 | 151 | 152 | 153 | ... 154 | 155 | 156 | ... 157 | 158 | 159 | 160 | `systemjs.config.js` 161 | 162 | Configure SystemJS to load `autopulous-angular2-soap` 163 | 164 | 'use strict'; 165 | 166 | System.config ({ 167 | // tell the SystemJS loader where to look for packages and components 168 | 169 | map: { 170 | ... 171 | 172 | 'autopulous-angular2-soap': 'vendor/autopulous-angular2-soap', 173 | 174 | ... 175 | }, 176 | 177 | // tell the SystemJS loader how to load when no filename and/or no extension 178 | 179 | packages: { 180 | ... 181 | 182 | 'autopulous-angular2-soap': { defaultExtension: 'js' }, 183 | 184 | ... 185 | } 186 | }); 187 | 188 | ## To modify/customize this package within your development environment 189 | 1: Clone the `Git` repository 190 | 191 | git clone https://github.com/autopulous/angular2-soap.git 192 | 2: Install packages (from the command line) 193 | 194 | npm install 195 | 3: Start build/test servers (from the command line) (invokes `Gulp` watcher and `Karma` auto refresh) 196 | 197 | npm run "unit test continuously" 198 | 3a: Alternatively: create/use your preferred build/test methodology to automate the development process 199 | 200 | ## To include this package within your project 201 | 1: Within your project's `package.json` file include the Angular2 SOAP client within the `dependencies` section 202 | 203 | "autopulous-angular2-soap": "0.4.7" 204 | 2: Implement calls to the package as illustrated in the example code (above) -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require ('gulp'); 4 | var runSequence = require ('run-sequence'); 5 | 6 | var node = './node_modules'; 7 | var src = './src'; 8 | var dist = './dist'; 9 | var webapp = './webapp'; 10 | 11 | var vendor = dist + '/vendor'; 12 | 13 | gulp.task ('delete-node-modules', function () { 14 | return clean ([node]); 15 | }); 16 | 17 | gulp.task ('clean', function () { 18 | return clean ([dist, webapp, './*.log']); 19 | }); 20 | 21 | function clean (patterns) { 22 | var del = require ('del'); 23 | 24 | console.log ('deleting: ' + patterns); 25 | console.log ('Note: should this fail you might have one of the directories open in the terminal'); 26 | 27 | return del (patterns); 28 | } 29 | 30 | gulp.task ('compile-modules', function () { 31 | var typescript = require ('gulp-typescript'); 32 | 33 | return gulp.src ([src + '/**/*.ts', '!' + src + '/**/spec*.ts', './typings.d.ts']) 34 | .pipe (typescript (typescript.createProject ('./tsconfig.json'))) 35 | .pipe (gulp.dest (dist + '/')); 36 | }); 37 | 38 | gulp.task ('compile-modules-with-maps', function () { 39 | var sourcemaps = require ('gulp-sourcemaps'); 40 | var typescript = require ('gulp-typescript'); 41 | 42 | return gulp.src ([src + '/**/*.ts', '!' + src + '/**/spec*.ts', './typings.d.ts']) 43 | .pipe (typescript (typescript.createProject ('./tsconfig.json'))) 44 | .pipe (sourcemaps.init ()) 45 | .pipe (sourcemaps.write ('./')) 46 | .pipe (gulp.dest (dist + '/')); 47 | }); 48 | 49 | gulp.task ('compile-tests', function () { 50 | var sourcemaps = require ('gulp-sourcemaps'); 51 | var typescript = require ('gulp-typescript'); 52 | 53 | return gulp.src ([src + '/**/spec*.ts', './typings.d.ts']) 54 | .pipe (typescript (typescript.createProject ('./tsconfig.json'))) 55 | .pipe (sourcemaps.init ()) 56 | .pipe (sourcemaps.write ('./')) 57 | .pipe (gulp.dest (dist + '/')); 58 | }); 59 | 60 | gulp.task ('compile-typings', function () { 61 | var sourcemaps = require ('gulp-sourcemaps'); 62 | var typescript = require ('gulp-typescript'); 63 | 64 | return gulp.src ([src + '/**/*.ts', '!' + src + '/**/spec*.ts', './typings.d.ts']) 65 | .pipe (typescript (typescript.createProject ('./tsconfig.json'))).dts 66 | .pipe (gulp.dest (dist + '/')); 67 | }); 68 | 69 | gulp.task ('copy-javascript', function () { 70 | return gulp.src ([src + '/**/*.js', '!' + src + '/karma.@angular.shim.js', '!' + src + '/systemjs.config.js']) 71 | .pipe (gulp.dest (dist + '/')); 72 | }); 73 | 74 | gulp.task ('copy-metadata', function () { 75 | return gulp.src (['./src/package.json', 'README.md']) 76 | .pipe (gulp.dest (dist + '/')); 77 | }); 78 | 79 | /* this task must be updated to correspond to the package.json for the third-party packages (node_modules) to include in the application distribution (vendor) */ 80 | 81 | gulp.task ('copy-vendor', function () { 82 | gulp.src ([node + '/autopulous-xdom/**/*.js']) 83 | .pipe (gulp.dest (vendor + '/autopulous-xdom')); 84 | 85 | return gulp.src ([node + '/autopulous-xdom2jso/**/*.js']) 86 | .pipe (gulp.dest (vendor + '/autopulous-xdom2jso')); 87 | }); 88 | 89 | // Do not automatically perform a 'clean' when rebuilding the tests because Karma tends to gets stuck when files that it is monitoring are deleted 90 | 91 | gulp.task ('rebuild-test', function () { 92 | runSequence ('compile-modules-with-maps', 'compile-tests', 'compile-css-with-maps', ['copy-html', 'copy-javascript', 'copy-images']); 93 | }); 94 | 95 | gulp.task ('watch-dev', function () { 96 | gulp.watch ([src + '/**/*.ts'], ['compile-modules']); 97 | gulp.watch ([src + '/**/*.ts'], ['compile-modules-with-maps']); 98 | gulp.watch ([src + '/**/*.scss'], ['compile-css']); 99 | gulp.watch ([src + '/**/*.scss'], ['compile-css-with-maps']); 100 | gulp.watch ([src + '/**/*.html'], ['copy-html']); 101 | gulp.watch ([src + '/**/*.js'], ['copy-javascript']); 102 | gulp.watch ([src + '/**/*.+(ico|gif|jpg|jpeg|png)'], ['copy-images']); 103 | }); 104 | 105 | gulp.task ('default', function () { 106 | gulp.watch ([src + '/**/*'], ['rebuild-test']); 107 | }); -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (config) { 4 | config.set({ 5 | basePath: './', 6 | 7 | files: [ 8 | // paths loaded by Karma 9 | {pattern: 'node_modules/es6-shim/es6-shim.js', included: true, watched: false}, 10 | {pattern: 'node_modules/zone.js/dist/zone.js', included: true, watched: false}, 11 | {pattern: 'node_modules/reflect-metadata/Reflect.js', included: true, watched: false}, 12 | 13 | {pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false}, 14 | {pattern: 'node_modules/@angular/**/*.js', included: false, watched: false}, 15 | 16 | {pattern: 'node_modules/systemjs/dist/system.src.js', included: true, watched: false}, 17 | {pattern: 'node_modules/autopulous-xdom/xdom.js', included: true, watched: false}, 18 | {pattern: 'node_modules/autopulous-xdom2jso/xdom2jso.js', included: true, watched: false}, 19 | 20 | {pattern: 'src/systemjs.config.js', included: true, watched: false}, 21 | 22 | // shim 23 | {pattern: 'src/karma.@angular.shim.js', included: true, watched: false}, 24 | 25 | // paths loaded via module imports 26 | {pattern: 'dist/**/*.js', served: true, included: false, watched: true}, 27 | 28 | // paths to support debugging with source maps in dev tools 29 | {pattern: 'src/**/*.ts', included: false, watched: false}, 30 | {pattern: 'map/**/*.js.map', included: false, watched: false} 31 | ], 32 | 33 | // proxied base paths 34 | proxies: { 35 | // required for component assets fetched by Angular 36 | '/vendor/': '/base/node_modules/' 37 | }, 38 | 39 | autoWatch: true, 40 | 41 | logLevel: config.LOG_DEBUG, 42 | 43 | frameworks: ['jasmine'], 44 | 45 | browsers: ['Chrome'], 46 | 47 | plugins: ['karma-jasmine', 'karma-chrome-launcher', 'karma-spec-reporter'], 48 | 49 | reporters: ["spec"], 50 | 51 | specReporter: { 52 | maxLogLines: 50, 53 | suppressFailed: false, 54 | suppressPassed: false, 55 | suppressSkipped: false, 56 | suppressErrorSummary: true 57 | } 58 | }); 59 | }; 60 | 61 | console.log('Reminder: if no tests ran, make sure that those were built and that the karma.@angular.shim.js file is in the app/ directory'); 62 | console.log(''); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autopulous-angular2-soap", 3 | "version": "0.4.7", 4 | "description": "Angular 2 SOAP client service", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/autopulous/angular2-soap.git" 8 | }, 9 | "license": "MIT", 10 | "private": false, 11 | "dependencies": { 12 | "@angular/common": "2.0.0-rc.4", 13 | "@angular/compiler": "2.0.0-rc.4", 14 | "@angular/core": "2.0.0-rc.4", 15 | "@angular/http": "2.0.0-rc.4", 16 | "@angular/platform-browser": "2.0.0-rc.4", 17 | "@angular/platform-browser-dynamic": "2.0.0-rc.4", 18 | "@angular/upgrade": "2.0.0-rc.4", 19 | "angular2-in-memory-web-api": "0.0.14", 20 | "autopulous-xdom": "latest", 21 | "autopulous-xdom2jso": "latest", 22 | "es6-shim": "0.35.1", 23 | "reflect-metadata": "0.1.3", 24 | "rxjs": "5.0.0-beta.6", 25 | "systemjs": "0.19.31", 26 | "zone.js": "0.6.12" 27 | }, 28 | "devDependencies": { 29 | "codelyzer": "0.0.24", 30 | "del": "2.2.1", 31 | "gulp": "3.9.1", 32 | "gulp-sourcemaps": "1.6.0", 33 | "gulp-typescript": "2.13.6", 34 | "http-server": "0.9.0", 35 | "jasmine-core": "2.4.1", 36 | "karma": "1.1.0", 37 | "karma-chrome-launcher": "1.0.1", 38 | "karma-jasmine": "1.0.2", 39 | "karma-spec-reporter": "0.0.26", 40 | "run-sequence": "1.2.2", 41 | "ts-node": "0.9.3", 42 | "tslint": "3.13.0", 43 | "typescript": "1.8.10", 44 | "typings": "1.3.1" 45 | }, 46 | "scripts": { 47 | "build package": "gulp clean && gulp compile-modules && gulp compile-typings && gulp copy-javascript && gulp copy-metadata && gulp copy-vendor", 48 | "build tests": "gulp clean && gulp compile-modules-with-maps && gulp compile-tests && gulp copy-javascript", 49 | "unit test": "karma start ./karma.conf.js --single-run", 50 | "unit test continuously": "karma start ./karma.conf.js", 51 | "build & unit test continuously": "gulp default", 52 | "npm (install/update packages)": "npm install && typings install", 53 | "npm (list instaled packages)": "npm ls", 54 | "npm (list outdated packages)": "npm outdated", 55 | "npm (simplify duplicate packages)": "npm dedupe", 56 | "npm (remove unused packages)": "npm prune", 57 | "npm (cache clear)": "npm cache clean", 58 | "npm (list cached packages)": "npm cache ls", 59 | "npm (remove all packages)": "gulp delete-node-modules", 60 | "npm (update npm version)": "npm install npm@latest -g" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/karma.@angular.shim.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Error.stackTraceLimit = Infinity; 4 | 5 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 6 | 7 | __karma__.loaded = function () {}; // Prevent Karma's synchronous start 8 | 9 | System.config({ 10 | packages: { 11 | 'base/app': { 12 | defaultExtension: false, 13 | format: 'register', 14 | map: Object.keys(window.__karma__.files).filter(onlyApplicationFiles).reduce(function createPathRecords(pathMap, applicationPath) { 15 | // creates local module name mapping to global path with karma's fingerprint in path 16 | var moduleName = applicationPath.replace(/^\/base\/app\//, './').replace(/\.js$/, ''); 17 | pathMap[moduleName] = applicationPath + '?' + window.__karma__.files[applicationPath]; 18 | return pathMap; 19 | }, {}) 20 | } 21 | } 22 | }); 23 | 24 | Promise.all( 25 | Object.keys(window.__karma__.files) // All files served by Karma. 26 | .filter(onlyTestFiles) 27 | .map(function (moduleName) { 28 | return System.import(moduleName); // loads all spec files via their global module names 29 | })) 30 | .then(function () { 31 | __karma__.start(); 32 | }, function (error) { 33 | __karma__.error(error.stack || error); 34 | }); 35 | 36 | function onlyApplicationFiles(filePath) { 37 | if (/\/base\/app\/(?!.*spec\.js$).*\.js$/.test(filePath)) console.log("Component filePath: " + filePath); 38 | return /\/base\/app\/(?!.*spec\.js$).*\.js$/.test(filePath); 39 | } 40 | 41 | function onlyTestFiles(filePath) { 42 | if (/spec\.js$/.test(filePath)) console.log("Test filePath: " + filePath); 43 | return /spec\.js$/.test(filePath); 44 | } -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "autopulous-angular2-soap", 3 | "version": "0.4.7", 4 | "description": "Angular 2 SOAP client service", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/autopulous/angular2-soap.git" 8 | }, 9 | "license": "MIT", 10 | "private": false, 11 | "main": "soap.service.js", 12 | "keywords": [ 13 | "XML", 14 | "SOAP", 15 | "ANGULAR 2", 16 | "@ANGULAR", 17 | "TypeScript" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/soap.service.ts: -------------------------------------------------------------------------------- 1 | import convert = xdom2jso.convert; 2 | 3 | import {Injectable} from '@angular/core'; 4 | 5 | @Injectable() 6 | export class SoapService { 7 | private debug:boolean = false; 8 | private asynchronous:boolean = true; 9 | private localName:boolean = false; 10 | 11 | private servicePort:string = ''; 12 | private servicePath:string = ''; 13 | private serviceUrl:string = ''; 14 | 15 | private targetNamespace:string = ''; 16 | 17 | private envelopeBuilder_:(requestBody:string) => string = null; 18 | private xmlResponseHandler_:(response:NodeListOf) => void = null; 19 | private jsoResponseHandler_:(response:{}) => void = null; 20 | 21 | constructor(servicePort:string, servicePath:string, targetNamespace?:string) { 22 | this.servicePort = servicePort; 23 | this.servicePath = servicePath; 24 | this.serviceUrl = servicePort + servicePath; 25 | 26 | if (undefined !== targetNamespace) this.targetNamespace = targetNamespace; 27 | } 28 | 29 | set envelopeBuilder(envelopeBuilder:(response:{}) => string) { 30 | this.envelopeBuilder_ = envelopeBuilder; 31 | } 32 | 33 | set jsoResponseHandler(responseHandler:(response:{}) => void) { 34 | this.jsoResponseHandler_ = responseHandler; 35 | } 36 | 37 | set xmlResponseHandler(responseHandler:(response:NodeListOf) => void) { 38 | this.xmlResponseHandler_ = responseHandler; 39 | } 40 | 41 | set localNameMode(on:boolean) { 42 | this.localName = on; 43 | } 44 | 45 | set debugMode(on:boolean) { 46 | this.debug = on; 47 | } 48 | 49 | set testMode(on:boolean) { 50 | this.debug = on; 51 | this.asynchronous = !on; 52 | } 53 | 54 | public post(method:string, parameters:any, responseRoot?:string):void { 55 | var request:string = this.toXml(parameters); 56 | var envelopedRequest:string = null != this.envelopeBuilder_ ? this.envelopeBuilder_(request) : request; 57 | 58 | if (this.debug) { 59 | console.log('target namespace: ' + this.targetNamespace); 60 | console.log('method: ' + method); 61 | console.log('service URL: ' + this.serviceUrl); 62 | console.log('request: ' + request); 63 | console.log('envelopedRequest: ' + envelopedRequest); 64 | console.log((this.asynchronous ? 'asynchronous' : 'synchronous') + ' ' + (this.localName ? 'without namespaces' : 'with namespaces (if returned by the webservice)')); 65 | } 66 | 67 | var xmlHttp:XMLHttpRequest = new XMLHttpRequest(); 68 | 69 | xmlHttp.onreadystatechange = () => { 70 | if (this.debug) { 71 | console.log('XMLHttpRequest ready state: ' + xmlHttp.readyState); 72 | } 73 | 74 | if (4 == xmlHttp.readyState) { 75 | if (this.debug) { 76 | console.log('XMLHttpRequest status: ' + xmlHttp.status); 77 | console.log('XMLHttpRequest status text: ' + xmlHttp.statusText); 78 | console.log('XMLHttpRequest response headers: ' + xmlHttp.getAllResponseHeaders()); 79 | } 80 | 81 | var responseNodeList:NodeListOf; 82 | 83 | if (undefined === responseRoot) { 84 | responseNodeList = xmlHttp.responseXML; 85 | } 86 | else { 87 | responseNodeList = xmlHttp.responseXML.getElementsByTagNameNS('*', responseRoot); 88 | } 89 | 90 | if (null != this.xmlResponseHandler_) { 91 | this.xmlResponseHandler_(responseNodeList); 92 | } 93 | 94 | if (null != this.jsoResponseHandler_) { 95 | var response:{} = convert(responseNodeList[0], this.localName); 96 | 97 | if (this.debug) { 98 | console.log(JSON.stringify(response)); 99 | } 100 | 101 | this.jsoResponseHandler_(response); 102 | } 103 | } 104 | }; 105 | 106 | xmlHttp.open("POST", this.serviceUrl, this.asynchronous); 107 | 108 | xmlHttp.setRequestHeader("SOAPAction", this.targetNamespace + '/' + encodeURIComponent(method)); 109 | xmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8"); 110 | 111 | xmlHttp.send(envelopedRequest); 112 | } 113 | 114 | private toXml(parameters:any):string { 115 | var xml:string = ""; 116 | var parameter:any; 117 | 118 | switch (typeof(parameters)) { 119 | case "string": 120 | xml += parameters.replace(/&/g, "&").replace(//g, ">"); 121 | break; 122 | 123 | case "number": 124 | case "boolean": 125 | xml += parameters.toString(); 126 | break; 127 | 128 | case "object": 129 | if (parameters.constructor.toString().indexOf("function Date()") > -1) { 130 | let year:string = parameters.getFullYear().toString(); 131 | let month:string = ("0" + (parameters.getMonth() + 1).toString()).slice(-2); 132 | let date:string = ("0" + parameters.getDate().toString()).slice(-2); 133 | let hours:string = ("0" + parameters.getHours().toString()).slice(-2); 134 | let minutes:string = ("0" + parameters.getMinutes().toString()).slice(-2); 135 | let seconds:string = ("0" + parameters.getSeconds().toString()).slice(-2); 136 | let milliseconds:string = parameters.getMilliseconds().toString(); 137 | 138 | let tzOffsetMinutes:number = Math.abs(parameters.getTimezoneOffset()); 139 | let tzOffsetHours:number = 0; 140 | 141 | while (tzOffsetMinutes >= 60) { 142 | tzOffsetHours++; 143 | tzOffsetMinutes -= 60; 144 | } 145 | 146 | let tzMinutes:string = ("0" + tzOffsetMinutes.toString()).slice(-2); 147 | let tzHours:string = ("0" + tzOffsetHours.toString()).slice(-2); 148 | 149 | let timezone:string = ((parameters.getTimezoneOffset() < 0) ? "-" : "+") + tzHours + ":" + tzMinutes; 150 | 151 | xml += year + "-" + month + "-" + date + "T" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + timezone; 152 | } 153 | else if (parameters.constructor.toString().indexOf("function Array()") > -1) { // Array 154 | for (parameter in parameters) { 155 | if (parameters.hasOwnProperty(parameter)) { 156 | if (!isNaN(parameter)) { // linear array 157 | (/function\s+(\w*)\s*\(/ig).exec(parameters[parameter].constructor.toString()); 158 | 159 | var type = RegExp.$1; 160 | 161 | switch (type) { 162 | case "": 163 | type = typeof(parameters[parameter]); 164 | break; 165 | case "String": 166 | type = "string"; 167 | break; 168 | case "Number": 169 | type = "int"; 170 | break; 171 | case "Boolean": 172 | type = "bool"; 173 | break; 174 | case "Date": 175 | type = "DateTime"; 176 | break; 177 | } 178 | xml += this.toElement(type, parameters[parameter]); 179 | } 180 | else { // associative array 181 | xml += this.toElement(parameter, parameters[parameter]); 182 | } 183 | } 184 | } 185 | } 186 | else { // Object or custom function 187 | for (parameter in parameters) { 188 | if (parameters.hasOwnProperty(parameter)) { 189 | xml += this.toElement(parameter, parameters[parameter]); 190 | } 191 | } 192 | } 193 | break; 194 | 195 | default: 196 | throw new Error("SoapService error: type '" + typeof(parameters) + "' is not supported"); 197 | } 198 | 199 | return xml; 200 | } 201 | 202 | private toElement(tagNamePotentiallyWithAttributes:string, parameters:any):string { 203 | var elementContent:string = this.toXml(parameters); 204 | 205 | if ("" == elementContent) { 206 | return "<" + tagNamePotentiallyWithAttributes + "/>"; 207 | } 208 | else { 209 | return "<" + tagNamePotentiallyWithAttributes + ">" + elementContent + ""; 210 | } 211 | } 212 | 213 | private static stripTagAttributes(tagNamePotentiallyWithAttributes:string):string { 214 | tagNamePotentiallyWithAttributes = tagNamePotentiallyWithAttributes + ' '; 215 | 216 | return tagNamePotentiallyWithAttributes.slice(0, tagNamePotentiallyWithAttributes.indexOf(' ')); 217 | } 218 | } -------------------------------------------------------------------------------- /src/spec.ts: -------------------------------------------------------------------------------- 1 | import {it, describe, expect, beforeEach, beforeEachProviders, inject} from '@angular/core/testing'; 2 | 3 | import {SoapService} from "./soap.service"; 4 | 5 | var method:string = 'ExecuteLogin'; 6 | var requestRoot:string = `ExecuteLoginRequest`; 7 | var responseRoot:string = `ExecuteLoginResponse`; 8 | 9 | var servicePort:string = 'http://172.16.62.123:8091'; 10 | var serviceRoot:string = '/vertex-enterprise-ws/ws/'; 11 | var targetNamespace:string = 'http://www.vertexinc.com/gto/schema/definitions'; 12 | 13 | var username:string = 'info@vertexSMB.com'; 14 | var password:string = 'OKMzaq123*()'; 15 | 16 | var soapService:SoapService = null; 17 | 18 | var responseJso:{} = null; 19 | var responseXml:NodeListOf = null; 20 | 21 | describe('SOAP SERVICE: unit tests', () => { 22 | describe('Prove that the unit tests are wired correctly', () => { 23 | it('Should prove that these tests are executing by verifying that true equals true', () => { 24 | expect(true).toEqual(true); 25 | }); 26 | }); 27 | 28 | describe('Prove that the service is instantiated correctly', () => { 29 | it('Should not throw any exceptions when setting configuration parameters', () => { 30 | soapService = new SoapService(servicePort, serviceRoot, targetNamespace); 31 | soapService.envelopeBuilder = envelopeBuilder; 32 | soapService.xmlResponseHandler = (response:NodeListOf) => {responseXml = response}; 33 | soapService.jsoResponseHandler = (response:{}) => {responseJso = response}; 34 | soapService.testMode = true; 35 | soapService.testMode = false; 36 | soapService.debugMode = true; 37 | soapService.debugMode = false; 38 | soapService.localNameMode = true; 39 | soapService.localNameMode = false; 40 | }); 41 | }); 42 | 43 | describe('Prove that SOAP requests work', () => { 44 | it('Should be able to gat a SOAP webservice response', () => { 45 | soapService = new SoapService(servicePort, serviceRoot); 46 | soapService.testMode = true; 47 | soapService.localNameMode = false; 48 | soapService.envelopeBuilder = envelopeBuilder; 49 | soapService.xmlResponseHandler = (response:NodeListOf) => {responseXml = response}; 50 | soapService.jsoResponseHandler = (response:{}) => {responseJso = response}; 51 | 52 | executeLogin(soapService, username, password); 53 | 54 | expect(responseXml).not.toBeNull(); 55 | expect(responseJso).not.toBeNull(); 56 | }); 57 | 58 | it('Should be able to get a namespace stripped the SOAP webservice response', () => { 59 | soapService = new SoapService(servicePort, serviceRoot, targetNamespace); 60 | soapService.testMode = true; 61 | soapService.localNameMode = true; 62 | soapService.envelopeBuilder = envelopeBuilder; 63 | soapService.xmlResponseHandler = (response:NodeListOf) => {responseXml = response}; 64 | soapService.jsoResponseHandler = (response:{}) => {responseJso = response}; 65 | 66 | executeLogin(soapService, username, password); 67 | 68 | expect(responseXml).not.toBeNull(); 69 | expect(responseJso).not.toBeNull(); 70 | }); 71 | }); 72 | }); 73 | 74 | function envelopeBuilder(requestBody:string):string { 75 | var soapRequest:string = 76 | "" + 77 | "" + 78 | "" + 79 | "" + 80 | "" + username + "" + 81 | "" + password + "" + 82 | "" + 83 | "" + 84 | "" + 85 | "" + 86 | requestBody + 87 | "" + 88 | ""; 89 | 90 | return soapRequest; 91 | } 92 | 93 | function executeLogin(soapService:SoapService, username:string, password:string) { 94 | var parameters:any = {}; 95 | 96 | parameters[requestRoot + ' xmlns="urn:vertexinc:enterprise:platform:security:messages:1:0"'] = executeLoginRequest(username, password); 97 | 98 | soapService.post(method, parameters, responseRoot); 99 | } 100 | 101 | function executeLoginRequest(username:string, password:string):{}[] { 102 | var parameters:any = {}; 103 | 104 | parameters['RequestContext xmlns="urn:vertexinc:enterprise:platform:common:1:0"'] = requestContext(); 105 | parameters['UserLogin xmlns="urn:vertexinc:enterprise:platform:security:1:0"'] = userLogin(username, password); 106 | 107 | return parameters; 108 | } 109 | 110 | function requestContext():{}[] { 111 | var parameters:any = {}; 112 | 113 | parameters["AsOfDate"] = todayString(); 114 | parameters['ApplicationName'] = 'VE'; 115 | parameters['ApplicationVersion'] = '6.0.0.04.00'; 116 | parameters['BrowserDate'] = todayString(); 117 | parameters['BrowserTimeZoneOffset'] = new Date().getTimezoneOffset(); 118 | 119 | return parameters; 120 | } 121 | 122 | function userLogin(username:string, password:string):{}[] { 123 | var parameters:any = {}; 124 | 125 | parameters["UserName"] = username; 126 | parameters['Password'] = password; 127 | 128 | return parameters; 129 | } 130 | 131 | function todayString():string { 132 | return dateString(new Date()); 133 | } 134 | 135 | function dateString(date:Date):string { 136 | var yyyy:string = date.getFullYear() + ''; 137 | 138 | var month:number = date.getMonth() + 1; 139 | var mm:string = (10 > month ? '0' : '') + month; 140 | 141 | var day:number = date.getDate(); 142 | var dd:string = (10 > day ? '0' : '') + day; 143 | 144 | return yyyy + '-' + mm + '-' + dd; 145 | } 146 | -------------------------------------------------------------------------------- /src/systemjs.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | System.config({ 4 | // tell the SystemJS loader where to look for packages and components 5 | 6 | map: { 7 | 'application': '.', 8 | '@angular': 'vendor/@angular', 9 | 'rxjs': 'vendor/rxjs' 10 | }, 11 | 12 | // tell the SystemJS loader how to load when no filename and/or no extension 13 | 14 | packages: { 15 | 'application': {defaultExtension: 'js'}, 16 | '@angular/common': {main:'index.js', defaultExtension: 'js'}, 17 | '@angular/compiler': {main:'index.js', defaultExtension: 'js'}, 18 | '@angular/core': {main:'index.js', defaultExtension: 'js'}, 19 | '@angular/http': {main:'index.js', defaultExtension: 'js'}, 20 | '@angular/platform-browser': {main:'index.js', defaultExtension: 'js'}, 21 | '@angular/platform-browser-dynamic': {main:'index.js', defaultExtension: 'js'}, 22 | '@angular/upgrade': {main:'index.js', defaultExtension: 'js'}, 23 | 'rxjs': {defaultExtension: 'js'} 24 | } 25 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "ES5", 5 | "module": "System", 6 | "moduleResolution": "node", 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "removeComments": true, 10 | "noImplicitAny": true, 11 | "declaration": true, 12 | "sourceMap": true, 13 | "mapRoot": "./", 14 | "diagnostics": true 15 | }, 16 | "exclude": [ 17 | "node_modules", 18 | "typings" 19 | ] 20 | } -------------------------------------------------------------------------------- /typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | declare var require: any; 5 | declare var module: { id: string }; -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalDevDependencies": { 3 | "jasmine": "registry:dt/jasmine#2.2.0+20160621224255" 4 | }, 5 | "globalDependencies": { 6 | "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504", 7 | "systemjs": "registry:dt/systemjs#0.19.29+20160702105636", 8 | "zone.js": "registry:dt/zone.js#0.0.0+20160316155526" 9 | } 10 | } --------------------------------------------------------------------------------