├── src ├── assets │ ├── .gitkeep │ └── calculator.wsdl ├── app │ ├── app.component.css │ ├── app.component.spec.ts │ ├── app.module.ts │ ├── app.component.ts │ └── app.component.html ├── favicon.ico ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── styles.css ├── tsconfig.app.json ├── tsconfig.spec.json ├── tslint.json ├── main.ts ├── index.html ├── test.ts ├── karma.conf.js └── polyfills.ts ├── projects └── ngx-soap │ ├── tsconfig.lib.prod.json │ ├── src │ ├── public_api.ts │ ├── lib │ │ ├── soap │ │ │ ├── security │ │ │ │ ├── templates │ │ │ │ │ ├── wsse-security-token.ejs │ │ │ │ │ └── wsse-security-header.ejs │ │ │ │ ├── security.ts │ │ │ │ ├── BearerSecurity.ts │ │ │ │ ├── BasicAuthSecurity.ts │ │ │ │ ├── NTLMSecurity.ts │ │ │ │ ├── ClientSSLSecurityPFX.js │ │ │ │ ├── ClientSSLSecurity.js │ │ │ │ ├── WSSecurity.ts │ │ │ │ └── WSSecurityCert.ts │ │ │ ├── utils.ts │ │ │ ├── soapAttachment.ts │ │ │ ├── multipart.ts │ │ │ ├── soap.ts │ │ │ ├── http.ts │ │ │ ├── interfaces.ts │ │ │ ├── nscontext.ts │ │ │ ├── _soap.d.ts │ │ │ └── client.ts │ │ ├── ngx-soap.module.ts │ │ ├── ngx-soap.service.ts │ │ └── ngx-soap.service.spec.ts │ └── test.ts │ ├── ng-package.prod.json │ ├── ng-package.json │ ├── tsconfig.spec.json │ ├── tslint.json │ ├── tsconfig.lib.json │ ├── package.json │ ├── karma.conf.js │ └── README.md ├── .gitignore ├── proxy.conf.json ├── e2e ├── src │ ├── app.po.ts │ └── app.e2e-spec.ts ├── tsconfig.e2e.json └── protractor.conf.js ├── .browserslistrc ├── tsconfig.json ├── LICENSE ├── README.md ├── CHANGELOG.md ├── package.json ├── tslint.json └── angular.json /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lula/ngx-soap/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | body { margin: 0; } 4 | -------------------------------------------------------------------------------- /projects/ngx-soap/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "angularCompilerOptions": { 4 | "enableIvy": false 5 | } 6 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | coverage 4 | yarn.lock 5 | .vscode 6 | .idea 7 | *.iml 8 | tools/bin 9 | tools/test 10 | dist 11 | spec/out -------------------------------------------------------------------------------- /projects/ngx-soap/src/public_api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-soap 3 | */ 4 | 5 | export * from './lib/ngx-soap.service'; 6 | export * from './lib/ngx-soap.module'; 7 | -------------------------------------------------------------------------------- /proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/calculator": { 3 | "target": "http://www.dneonline.com", 4 | "secure": false, 5 | "changeOrigin": "true", 6 | "pathRewrite": {"^/calculator": ""} 7 | } 8 | } -------------------------------------------------------------------------------- /projects/ngx-soap/ng-package.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-soap", 4 | "lib": { 5 | "entryFile": "src/public_api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /projects/ngx-soap/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-soap", 4 | "deleteDestPath": false, 5 | "lib": { 6 | "entryFile": "src/public_api.ts" 7 | } 8 | } -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/templates/wsse-security-token.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/ngx-soap.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { HttpClientModule } from '@angular/common/http'; 3 | 4 | @NgModule({ 5 | imports: [ 6 | HttpClientModule 7 | ], 8 | exports: [] 9 | }) 10 | export class NgxSoapModule { } 11 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "main.ts", 9 | "polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /projects/ngx-soap/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /projects/ngx-soap/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/security.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { BasicAuthSecurity } from './BasicAuthSecurity'; 4 | import { WSSecurity } from './WSSecurity'; 5 | // import { WSSecurityCert } from './WSSecurityCert'; 6 | import { BearerSecurity } from './BearerSecurity'; 7 | import { NTLMSecurity } from './NTLMSecurity'; 8 | 9 | export const security = { 10 | BasicAuthSecurity, 11 | BearerSecurity, 12 | WSSecurity, 13 | // WSSecurityCert, 14 | NTLMSecurity, 15 | // ClientSSLSecurity, 16 | // ClientSSLSecurityPFX 17 | }; -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | NgxSoapLib 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/BearerSecurity.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // var _ = require('lodash'); 4 | import * as _ from 'lodash'; 5 | 6 | export function BearerSecurity(token, defaults) { 7 | this._token = token; 8 | this.defaults = {}; 9 | _.merge(this.defaults, defaults); 10 | } 11 | 12 | BearerSecurity.prototype.addHeaders = function(headers) { 13 | headers.Authorization = "Bearer " + this._token; 14 | }; 15 | 16 | BearerSecurity.prototype.toXML = function() { 17 | return ''; 18 | }; 19 | 20 | BearerSecurity.prototype.addOptions = function(options) { 21 | _.merge(options, this.defaults); 22 | }; 23 | 24 | // module.exports = BearerSecurity; 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "downlevelIteration": true, 6 | "module": "es2020", 7 | "outDir": "./dist/out-tsc", 8 | "sourceMap": true, 9 | "declaration": false, 10 | "moduleResolution": "node", 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2017", 19 | "dom" 20 | ], 21 | "paths": { 22 | "ngx-soap": [ 23 | "dist/ngx-soap" 24 | ], 25 | "ngx-soap/*": [ 26 | "dist/ngx-soap/*" 27 | ] 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * In development mode, to ignore zone related error stack frames such as 11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 12 | * import the following file, but please comment it out in production mode 13 | * because it will have performance impact when throw error 14 | */ 15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 16 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/BasicAuthSecurity.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import { Buffer } from 'buffer'; 3 | 4 | export function BasicAuthSecurity(username, password, defaults) { 5 | this._username = username; 6 | this._password = password; 7 | this.defaults = {}; 8 | _.merge(this.defaults, defaults); 9 | } 10 | 11 | BasicAuthSecurity.prototype.addHeaders = function(headers) { 12 | headers.Authorization = 'Basic ' + new Buffer((this._username + ':' + this._password) || '').toString('base64'); 13 | }; 14 | 15 | BasicAuthSecurity.prototype.toXML = function() { 16 | return ''; 17 | }; 18 | 19 | BasicAuthSecurity.prototype.addOptions = function(options) { 20 | _.merge(options, this.defaults); 21 | }; 22 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js/dist/zone'; 5 | import 'zone.js/dist/zone-testing'; 6 | import { getTestBed } from '@angular/core/testing'; 7 | import { 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting 10 | } from '@angular/platform-browser-dynamic/testing'; 11 | 12 | declare const require: any; 13 | 14 | // First, initialize the Angular testing environment. 15 | getTestBed().initTestEnvironment( 16 | BrowserDynamicTestingModule, 17 | platformBrowserDynamicTesting() 18 | ); 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | // And load the modules. 22 | context.keys().map(context); 23 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/ngx-soap.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { createClient } from './soap/soap'; 3 | import { HttpClient } from '@angular/common/http'; 4 | import { Client } from './soap/interfaces'; 5 | 6 | export { 7 | Client, 8 | WSDL, 9 | ISoapMethod, 10 | ISoapMethodResponse, 11 | BasicAuthSecurity, 12 | BearerSecurity, 13 | // WSSecurityCert, 14 | WSSecurity, 15 | NTLMSecurity 16 | } from './soap/interfaces'; 17 | 18 | export { security } from './soap/security/security' 19 | 20 | @Injectable({ 21 | providedIn: 'root' 22 | }) 23 | export class NgxSoapService { 24 | 25 | constructor(private http: HttpClient) { } 26 | 27 | createClient(wsdlUrl: string, options: any = {}, endpoint?: string): Promise { 28 | options.httpClient = this.http; 29 | return createClient(wsdlUrl, options, endpoint); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/utils.ts: -------------------------------------------------------------------------------- 1 | import sha1 from 'crypto-js/sha1'; 2 | import Base64 from 'crypto-js/enc-base64'; 3 | import { Buffer } from 'buffer'; 4 | 5 | export const passwordDigest = function passwordDigest(nonce, created, password) { 6 | const rawNonce = new Buffer(nonce || '', 'base64').toString('binary'); 7 | return Base64.stringify(sha1(rawNonce + created + password, '')); 8 | }; 9 | 10 | export const TNS_PREFIX = '__tns__'; // Prefix for targetNamespace 11 | 12 | /** 13 | * Find a key from an object based on the value 14 | * @param Namespace prefix/uri mapping 15 | * @param nsURI value 16 | * @returns The matching key 17 | */ 18 | export const findPrefix = function(xmlnsMapping, nsURI) { 19 | for (const n in xmlnsMapping) { 20 | if (n === TNS_PREFIX) { continue; } 21 | if (xmlnsMapping[n] === nsURI) { 22 | return n; 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/NTLMSecurity.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // var _ = require('lodash'); 4 | import * as _ from 'lodash'; 5 | 6 | export function NTLMSecurity(username, password, domain, workstation) { 7 | if (typeof username === "object") { 8 | this.defaults = username; 9 | this.defaults.ntlm = true; 10 | } else { 11 | this.defaults = { 12 | ntlm: true, 13 | username: username, 14 | password: password, 15 | domain: domain, 16 | workstation: workstation 17 | }; 18 | } 19 | } 20 | 21 | NTLMSecurity.prototype.addHeaders = function (headers) { 22 | headers.Connection = 'keep-alive'; 23 | }; 24 | 25 | NTLMSecurity.prototype.toXML = function () { 26 | return ''; 27 | }; 28 | 29 | NTLMSecurity.prototype.addOptions = function (options) { 30 | _.merge(options, this.defaults); 31 | }; 32 | 33 | // module.exports = NTLMSecurity; 34 | -------------------------------------------------------------------------------- /projects/ngx-soap/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2015" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "skipTemplateCodegen": true, 22 | "strictMetadataEmit": true, 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true, 25 | "flatModuleId": "AUTOGENERATED", 26 | "flatModuleOutFile": "AUTOGENERATED" 27 | }, 28 | "exclude": [ 29 | "src/test.ts", 30 | "**/*.spec.ts" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/templates/wsse-security-header.ejs: -------------------------------------------------------------------------------- 1 | 4 | <%-binaryToken%> 8 | 9 | <%-created%> 10 | <%-expires%> 11 | 12 | 13 | -------------------------------------------------------------------------------- /projects/ngx-soap/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-soap", 3 | "version": "0.7.0", 4 | "description": "SOAP service for Angular", 5 | "homepage": "https://github.com/lula/ngx-soap", 6 | "author": "Luca Lulani", 7 | "license": "MIT", 8 | "keywords": [ 9 | "Angular", 10 | "Angular2", 11 | "Angular4", 12 | "Angular5", 13 | "Angular6", 14 | "Angular7", 15 | "Angular8", 16 | "Angular9", 17 | "Angular10", 18 | "@angular", 19 | "SOAP", 20 | "WSDL" 21 | ], 22 | "dependencies": { 23 | "tslib": "^2.0.0" 24 | }, 25 | "peerDependencies": { 26 | "@angular/common": ">=6.0.0 <=10.0.0", 27 | "@angular/core": ">=6.0.0 <=10.0.0", 28 | "buffer": "^5.1.0", 29 | "concat-stream": "^1.6.2", 30 | "core-js": "^2.5.4", 31 | "crypto-js": "^3.1.9-1", 32 | "events": "^3.0.0", 33 | "lodash": "^4.17.10", 34 | "rxjs": "^6.0.0", 35 | "sax": "^1.2.4", 36 | "stream": "0.0.2", 37 | "uuid": "^3.3.2" 38 | } 39 | } -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/soapAttachment.ts: -------------------------------------------------------------------------------- 1 | export class SoapAttachment { 2 | 3 | constructor( 4 | public mimetype: string, 5 | public contentId: string, 6 | public name: string, 7 | public body: any 8 | ) { 9 | 10 | } 11 | 12 | static fromFormFiles(files: FileList | File[] = []): Promise { 13 | if (files instanceof FileList) { 14 | files = Array.from(files); 15 | } 16 | 17 | const promises = files.map((file: any) => { 18 | return new Promise(function(resolve) { 19 | const reader = new FileReader(); 20 | reader.readAsArrayBuffer(file); 21 | reader.onload = function (e) { 22 | const arrayBuffer = (e.target as any).result; 23 | const bytes = new Uint8Array(arrayBuffer); 24 | const attachment = new SoapAttachment(file.type, file.contentId || file.name, file.name, bytes); 25 | resolve(attachment); 26 | } 27 | }); 28 | }); 29 | 30 | return Promise.all(promises); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | describe('AppComponent', () => { 4 | beforeEach(async(() => { 5 | TestBed.configureTestingModule({ 6 | declarations: [ 7 | AppComponent 8 | ], 9 | }).compileComponents(); 10 | })); 11 | it('should create the app', async(() => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.debugElement.componentInstance; 14 | expect(app).toBeTruthy(); 15 | })); 16 | it(`should have as title 'app'`, async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app.title).toEqual('app'); 20 | })); 21 | it('should render title in a h1 tag', async(() => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | fixture.detectChanges(); 24 | const compiled = fixture.debugElement.nativeElement; 25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); 26 | })); 27 | }); 28 | -------------------------------------------------------------------------------- /projects/ngx-soap/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Luca Lulani 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | proxies: { 31 | '/calculator': { 32 | 'target': "http://www.dneonline.com", 33 | 'secure': false, 34 | 'changeOrigin': true, 35 | 'pathRewrite': {"^/calculator": ""} 36 | } 37 | } 38 | }); 39 | }; -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { HttpClientModule } from '@angular/common/http'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatCardModule } from '@angular/material/card'; 7 | import { MatFormFieldModule } from '@angular/material/form-field'; 8 | import { MatInputModule } from '@angular/material/input'; 9 | import { MatProgressBarModule } from '@angular/material/progress-bar'; 10 | import { MatToolbarModule } from '@angular/material/toolbar'; 11 | import { FormsModule } from '@angular/forms'; 12 | 13 | import { NgxSoapModule } from 'ngx-soap'; 14 | import { AppComponent } from './app.component'; 15 | import { FlexLayoutModule } from '@angular/flex-layout'; 16 | 17 | const ANGULAR_MATERIAL_MODULES = [ 18 | MatToolbarModule, MatInputModule, MatButtonModule, MatCardModule, 19 | MatProgressBarModule, MatFormFieldModule 20 | ]; 21 | 22 | @NgModule({ 23 | declarations: [ 24 | AppComponent 25 | ], 26 | imports: [ 27 | BrowserModule, 28 | FormsModule, 29 | NgxSoapModule, 30 | HttpClientModule, 31 | BrowserAnimationsModule, 32 | FlexLayoutModule, 33 | ...ANGULAR_MATERIAL_MODULES 34 | ], 35 | providers: [], 36 | bootstrap: [AppComponent] 37 | }) 38 | export class AppModule { } 39 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/ClientSSLSecurityPFX.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs') 4 | , https = require('https') 5 | , _ = require('lodash'); 6 | 7 | /** 8 | * activates SSL for an already existing client using a PFX cert 9 | * 10 | * @module ClientSSLSecurityPFX 11 | * @param {Buffer|String} pfx 12 | * @param {String} passphrase 13 | * @constructor 14 | */ 15 | function ClientSSLSecurityPFX(pfx, passphrase, defaults) { 16 | if (typeof passphrase === 'object') { 17 | defaults = passphrase; 18 | } 19 | if (pfx) { 20 | if (Buffer.isBuffer(pfx)) { 21 | this.pfx = pfx; 22 | } else if (typeof pfx === 'string') { 23 | this.pfx = fs.readFileSync(pfx); 24 | } else { 25 | throw new Error('supplied pfx file should be a buffer or a file location'); 26 | } 27 | } 28 | 29 | if (passphrase) { 30 | if (typeof passphrase === 'string') { 31 | this.passphrase = passphrase; 32 | } 33 | } 34 | this.defaults = {}; 35 | _.merge(this.defaults, defaults); 36 | } 37 | 38 | ClientSSLSecurityPFX.prototype.toXML = function(headers) { 39 | return ''; 40 | }; 41 | 42 | ClientSSLSecurityPFX.prototype.addOptions = function(options) { 43 | options.pfx = this.pfx; 44 | if (this.passphrase) { 45 | options.passphrase = this.passphrase; 46 | } 47 | _.merge(options, this.defaults); 48 | options.agent = new https.Agent(options); 49 | }; 50 | 51 | module.exports = ClientSSLSecurityPFX; 52 | -------------------------------------------------------------------------------- /projects/ngx-soap/README.md: -------------------------------------------------------------------------------- 1 | # ngx-soap 2 | 3 | Simple SOAP client for Angular 6/7 based on amazing [node-soap](https://github.com/vpulim/node-soap). 4 | 5 | Project has been recreated from scratch with Angualr 6 CLI. 6 | 7 | ## npm 8 | 9 | 1. install ngx-soap and dependencies 10 | 11 | `npm install --save ngx-soap` 12 | 13 | `npm install --save buffer concat-stream core-js crypto-js events lodash sax stream uuid` 14 | 15 | 2. Add NgxSoapModule to your app module 16 | 17 | ``` 18 | import { NgxSoapModule } from 'ngx-soap'; 19 | ... 20 | @NgModule({ 21 | imports: [ ..., NgxSoapModule, ... ] 22 | ... 23 | ``` 24 | 25 | 3. Inject NgxSoapService in your component: 26 | 27 | ``` 28 | ... 29 | import { NgxSoapService, Client, ISoapMethodResponse } from 'ngx-soap'; 30 | ... 31 | 32 | @Component({ 33 | selector: 'app-root', 34 | templateUrl: './app.component.html', 35 | styleUrls: ['./app.component.css'] 36 | }) 37 | export class AppComponent { 38 | client: Client; 39 | intA = 2; 40 | intB = 3; 41 | 42 | constructor(private soap: NgxSoapService) { 43 | this.soap.createClient('assets/calculator.wsdl').subscribe(client => this.client = client); 44 | } 45 | 46 | sum() { 47 | const body = { 48 | intA: this.intA, 49 | intB: this.intB 50 | }; 51 | (this.client).Add(body).subscribe((res: ISoapMethodResponse) => this.message = res.result.AddResult); 52 | } 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/multipart.ts: -------------------------------------------------------------------------------- 1 | export class Multipart { 2 | preambleCRLF = true; 3 | postambleCRLF = true; 4 | 5 | build(parts, boundary) { 6 | const body = []; 7 | 8 | function add (part) { 9 | if (typeof part === 'number') { 10 | part = part.toString(); 11 | } 12 | return body.push(part) 13 | } 14 | 15 | if (this.preambleCRLF) { 16 | add('\r\n') 17 | } 18 | 19 | parts.forEach(function (part) { 20 | let preamble = '--' + boundary + '\r\n'; 21 | Object.keys(part).forEach(function (key) { 22 | if (key === 'body') { return } 23 | preamble += key + ': ' + part[key] + '\r\n' 24 | }); 25 | preamble += '\r\n'; 26 | add(preamble); 27 | add(part.body); 28 | add('\r\n'); 29 | }); 30 | add('--' + boundary + '--'); 31 | 32 | if (this.postambleCRLF) { 33 | add('\r\n'); 34 | } 35 | 36 | const size = body.map((part) => { 37 | if (typeof part === 'string') { 38 | return part.length 39 | } else { 40 | return part.byteLength; 41 | } 42 | }).reduce((a, b) => a + b, 0); 43 | 44 | let uint8array = new Uint8Array(size); 45 | let i = 0; 46 | body.forEach((part) => { 47 | if (typeof part === 'string') { 48 | for (let j = 0; j < part.length; i++, j++) { 49 | uint8array[i] = part.charCodeAt(j) & 0xff; 50 | } 51 | } else { 52 | for (let j = 0; j < part.byteLength; i++, j++) { 53 | uint8array[i] = part[j]; 54 | } 55 | } 56 | }); 57 | return uint8array.buffer; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/soap.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Vinay Pulim 3 | * MIT Licensed 4 | */ 5 | 6 | import * as wsdl from './wsdl'; 7 | import { security } from './security/security'; 8 | import { Client } from './client'; 9 | export { Client } from './client'; 10 | export { security } from './security/security'; 11 | 12 | export { passwordDigest } from './utils' 13 | export const WSDL = wsdl.WSDL; 14 | 15 | const cache = {}; // TODO some caching? 16 | 17 | const getFromCache = async (url, options) => { 18 | // console.log('Getting from cache', url); 19 | // console.log('Cache', cache) 20 | if (cache[url]) { 21 | // console.log('Found in cache', url); 22 | return cache[url]; 23 | } else { 24 | return wsdl.open_wsdl(url, options).then(wsdl => { 25 | cache[url] = wsdl; 26 | return wsdl; 27 | }) 28 | } 29 | }; 30 | 31 | async function _requestWSDL(url, options) { 32 | if (options.disableCache === true) { 33 | return wsdl.open_wsdl(url, options); 34 | } else { 35 | return getFromCache(url, options); 36 | } 37 | } 38 | 39 | export async function createClient(url, options, endpoint): Promise { 40 | if (typeof options === 'undefined') { 41 | options = {}; 42 | } 43 | // console.log("createClient", options) 44 | endpoint = options.endpoint || endpoint; 45 | 46 | const wsdl = await _requestWSDL(url, options); 47 | const client = new Client(wsdl, endpoint, options); 48 | return client; 49 | } 50 | 51 | export const BasicAuthSecurity = security.BasicAuthSecurity; 52 | export const NTLMSecurity = security.NTLMSecurity; 53 | export const WSSecurity = security.WSSecurity; 54 | // export const WSSecurityCert = security.WSSecurityCert; 55 | export const BearerSecurity = security.BearerSecurity; 56 | // export const ClientSSLSecurity = security.ClientSSLSecurity; 57 | // export const ClientSSLSecurityPFX = security.ClientSSLSecurityPFX; 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ngx-soap 2 | 3 | Simple SOAP client for Angular 6/10 based on amazing [node-soap](https://github.com/vpulim/node-soap). 4 | 5 | Project has been recreated from scratch with Angualr 6 CLI. 6 | 7 | ## npm 8 | 9 | 1. install ngx-soap and dependencies 10 | 11 | `npm install --save ngx-soap` 12 | 13 | `npm install --save buffer concat-stream core-js crypto-js events lodash sax stream uuid` 14 | 15 | 2. Add NgxSoapModule to your app module 16 | 17 | ``` 18 | import { NgxSoapModule } from 'ngx-soap'; 19 | ... 20 | @NgModule({ 21 | imports: [ ..., NgxSoapModule, ... ] 22 | ... 23 | ``` 24 | 25 | 3. Inject NgxSoapService in your component: 26 | 27 | ``` 28 | ... 29 | import { NgxSoapService, Client, ISoapMethodResponse } from 'ngx-soap'; 30 | ... 31 | 32 | @Component({ 33 | selector: 'app-root', 34 | templateUrl: './app.component.html', 35 | styleUrls: ['./app.component.css'] 36 | }) 37 | export class AppComponent { 38 | client: Client; 39 | intA = 2; 40 | intB = 3; 41 | 42 | constructor(private soap: NgxSoapService) { 43 | this.soap.createClient('assets/calculator.wsdl').subscribe(client => this.client = client); 44 | } 45 | 46 | sum() { 47 | const body = { 48 | intA: this.intA, 49 | intB: this.intB 50 | }; 51 | (this.client).Add(body).subscribe((res: ISoapMethodResponse) => this.message = res.result.AddResult); 52 | } 53 | } 54 | ``` 55 | 56 | 57 | ## Local development 58 | 59 | 1. `git clone -b angular6-cli-ilb https://github.com/lula/ngx-soap.git` 60 | 2. `cd ngx-soap && npm install` 61 | 3. `ng build ngx-soap` 62 | 4. `ng test ngx-soap` 63 | 5. `ng serve --proxy-config proxy.conf.json` 64 | 65 | See example app under `src/app` 66 | 67 | ## Contributors 68 | [Andrei Bespamiatnov](https://github.com/AndreyBespamyatnov) 69 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.5.0-beta.7 4 | 5 | Client events have been removed due to problems with Angular prod bundle. See issue [#29](https://github.com/lula/ngx-soap/issues/29). 6 | 7 | If you have used in your project you should remain on beta 6 release. Just remember to turn angular bundler optimization off if you want to bundle your project with --prod option. Also, in this case, please inform me so that I know events were actually used, and I'll consider to reintroduce them. 8 | 9 | ## 0.5.0-beta.6 10 | 11 | Export security classes. ([commit 4b48395](https://github.com/lula/ngx-soap/commit/4b483952c31880ad837ae92f209f06666291ff90)) 12 | 13 | ## 0.5.0-beta.5 14 | 15 | Raise error in case of calling a non existing method ([commit 77cf177](https://github.com/lula/ngx-soap/commit/77cf1772c4d042872b3326b28993bcbb0a5182c4)) 16 | 17 | ## 0.5.0-beta.4 18 | 19 | Use Angular HttpClient. 20 | Observables used wherever possible. 21 | 22 | this.soap.createClient('assets/calculator.wsdl').subscribe(client => this.client = client); 23 | 24 | (this.client).Add(body).subscribe((res: ISoapMethodResponse) => this.message = res.result.AddResult); 25 | 26 | this.client.call('Add', body).subscribe((res: ISoapMethodResponse) => this.message = res.result.AddResult); 27 | 28 | ## 0.3.0-beta1 29 | 30 | Project recreated with Angualr 6 CLI. 31 | 32 | ... 33 | 34 | ## 0.2.2-beta6 35 | Call operation with client method. 36 | 37 | ## 0.2.2-beta3 38 | 39 | ### Breaking Changes 40 | 41 | Web Service operations have no callback anymore. Callback has been replaced by a Promise. 42 | 43 | Before: 44 | 45 | (client as any).Add(input, (err, wsurl: string, headers: any, xml: string) => ... ) 46 | 47 | After: 48 | 49 | client.operation('Add', body).then((operation: Operation) => ... ) 50 | // or 51 | (client as any).Add(body).then((operation: Operation) => ... ) 52 | 53 | ## 0.2.1 54 | 55 | AOT compilation fixes (issue #1) 56 | 57 | ## 0.1.4 58 | 59 | Initial version 60 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NgxSoapService, ISoapMethod, Client, ISoapMethodResponse } from 'ngx-soap'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.css'] 8 | }) 9 | export class AppComponent { 10 | intA: number; 11 | intB: number; 12 | loading: boolean; 13 | showDiagnostic: boolean; 14 | message: string; 15 | xmlResponse: string; 16 | jsonResponse: string; 17 | resultLabel: string; 18 | client: Client; 19 | 20 | constructor(private soap: NgxSoapService) { 21 | this.soap.createClient('assets/calculator.wsdl') 22 | .then(client => { 23 | console.log('Client', client); 24 | this.client = client; 25 | }) 26 | .catch(err => console.log('Error', err)); 27 | } 28 | 29 | sum() { 30 | this.loading = true; 31 | const body = { 32 | intA: this.intA, 33 | intB: this.intB 34 | }; 35 | 36 | this.client.call('Add', body).subscribe(res => { 37 | this.xmlResponse = res.responseBody; 38 | this.message = res.result.AddResult; 39 | this.loading = false; 40 | }, err => console.log(err)); 41 | 42 | // OR: 43 | // (this.client).Add(body).subscribe( 44 | // (res: ISoapMethodResponse) => { 45 | // console.log('method response', res); 46 | // this.xmlResponse = res.xml; 47 | // this.message = res.result.AddResult; 48 | // this.loading = false; 49 | // }, 50 | // err => console.log(err) 51 | // ); 52 | } 53 | 54 | subtract() { 55 | this.loading = true; 56 | const body = { 57 | intA: this.intA, 58 | intB: this.intB 59 | }; 60 | (this.client).Subtract(body).subscribe( 61 | (res: ISoapMethodResponse) => { 62 | console.log('method response', res); 63 | this.xmlResponse = res.xml; 64 | this.message = res.result.SubtractResult; 65 | this.loading = false; 66 | }, 67 | err => console.log(err) 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/ClientSSLSecurity.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs') 4 | , https = require('https') 5 | , _ = require('lodash'); 6 | 7 | /** 8 | * activates SSL for an already existing client 9 | * 10 | * @module ClientSSLSecurity 11 | * @param {Buffer|String} key 12 | * @param {Buffer|String} cert 13 | * @param {Buffer|String|Array} [ca] 14 | * @param {Object} [defaults] 15 | * @constructor 16 | */ 17 | function ClientSSLSecurity(key, cert, ca, defaults) { 18 | if (key) { 19 | if(Buffer.isBuffer(key)) { 20 | this.key = key; 21 | } else if (typeof key === 'string') { 22 | this.key = fs.readFileSync(key); 23 | } else { 24 | throw new Error('key should be a buffer or a string!'); 25 | } 26 | } 27 | 28 | if (cert) { 29 | if(Buffer.isBuffer(cert)) { 30 | this.cert = cert; 31 | } else if (typeof cert === 'string') { 32 | this.cert = fs.readFileSync(cert); 33 | } else { 34 | throw new Error('cert should be a buffer or a string!'); 35 | } 36 | } 37 | 38 | if (ca) { 39 | if(Buffer.isBuffer(ca) || Array.isArray(ca)) { 40 | this.ca = ca; 41 | } else if (typeof ca === 'string') { 42 | this.ca = fs.readFileSync(ca); 43 | } else { 44 | defaults = ca; 45 | this.ca = null; 46 | } 47 | } 48 | 49 | this.defaults = {}; 50 | _.merge(this.defaults, defaults); 51 | 52 | this.agent = null; 53 | } 54 | 55 | ClientSSLSecurity.prototype.toXML = function(headers) { 56 | return ''; 57 | }; 58 | 59 | ClientSSLSecurity.prototype.addOptions = function(options) { 60 | var httpsAgent = null; 61 | 62 | options.key = this.key; 63 | options.cert = this.cert; 64 | options.ca = this.ca; 65 | _.merge(options, this.defaults); 66 | 67 | if (!!options.forever) { 68 | if (!this.agent) { 69 | options.keepAlive = true; 70 | 71 | this.agent = new https.Agent(options); 72 | } 73 | 74 | httpsAgent = this.agent; 75 | } else { 76 | httpsAgent = new https.Agent(options); 77 | } 78 | 79 | options.agent = httpsAgent; 80 | }; 81 | 82 | module.exports = ClientSSLSecurity; 83 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | SOAP Calculator app 3 | 4 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | Operation 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Result 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | 43 |
44 | 45 | 46 | XML Response 47 | 48 | 49 | {{ xmlResponse }} 50 | 51 | 52 | 53 | 54 | JSON Response 55 | 56 | 57 |
{{ jsonResponse | json }}
58 |
59 |
60 |
61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-soap", 3 | "version": "0.7.0", 4 | "description": "SOAP service for Angular4", 5 | "main": "src/index.js", 6 | "module": "src/index.js", 7 | "jsnext:main": "src/index.js", 8 | "typings": "src/index.d.ts", 9 | "moduleName": "ngx-soap", 10 | "scripts": { 11 | "transpile": "ngc", 12 | "bundle": "rimraf dist && npm run transpile && rollup -c rollup.config.umd.js", 13 | "postbundle": "node tools/bundle-scripts/remove-code.js", 14 | "build": "npm run bundle && npm run copy", 15 | "copy": "node tools/bundle-scripts/copy-files.js && node tools/bundle-scripts/cleanup.js", 16 | "test": "rimraf spec/out && tsc -p tsconfig.spec.json && jasmine", 17 | "build:lib": "ng build --prod ngx-soap && cp README.md dist/ngx-soap && cp LICENSE dist/ngx-soap" 18 | }, 19 | "keywords": [ 20 | "Angular", 21 | "Angular2", 22 | "Angular4", 23 | "Angular5", 24 | "Angular6", 25 | "Angular7", 26 | "@angular", 27 | "SOAP", 28 | "WSDL" 29 | ], 30 | "author": "Luca Lulani", 31 | "license": "MIT", 32 | "readme": "README.md", 33 | "homepage": "https://github.com/lula/ngx-soap", 34 | "bugs": { 35 | "url": "https://github.com/lula/ngx-soap/issues" 36 | }, 37 | "repository": { 38 | "type": "git", 39 | "url": "https://github.com/lula/ngx-soap.git" 40 | }, 41 | "dependencies": { 42 | "@angular/animations": "^10.1.2", 43 | "@angular/cdk": "^10.2.2", 44 | "@angular/common": "^10.1.2", 45 | "@angular/compiler": "^10.1.2", 46 | "@angular/core": "^10.1.2", 47 | "@angular/flex-layout": "^10.0.0-beta.32", 48 | "@angular/forms": "^10.1.2", 49 | "@angular/material": "^10.2.2", 50 | "@angular/platform-browser": "^10.1.2", 51 | "@angular/platform-browser-dynamic": "^10.1.2", 52 | "@angular/router": "^10.1.2", 53 | "buffer": "^5.1.0", 54 | "concat-stream": "^1.6.2", 55 | "core-js": "^2.5.4", 56 | "crypto-js": "^3.1.9-1", 57 | "events": "^3.0.0", 58 | "httpntlm": "^1.7.6", 59 | "lodash": "^4.17.10", 60 | "rxjs": "^6.6.3", 61 | "sax": "^1.2.4", 62 | "stream": "0.0.2", 63 | "tslib": "^2.0.0", 64 | "uuid": "^3.3.2", 65 | "xml-crypto": "^1.5.3", 66 | "zone.js": "~0.10.2" 67 | }, 68 | "devDependencies": { 69 | "@angular-devkit/build-angular": "~0.1001.2", 70 | "@angular-devkit/build-ng-packagr": "~0.1001.2", 71 | "@angular/cli": "~10.1.2", 72 | "@angular/compiler-cli": "^10.1.2", 73 | "@angular/language-service": "^10.1.2", 74 | "@types/jasmine": "~2.8.6", 75 | "@types/jasminewd2": "~2.0.3", 76 | "@types/node": "^12.11.1", 77 | "codelyzer": "^5.1.2", 78 | "jasmine-core": "~3.5.0", 79 | "jasmine-spec-reporter": "~5.0.0", 80 | "karma": "~5.0.0", 81 | "karma-chrome-launcher": "~3.1.0", 82 | "karma-coverage-istanbul-reporter": "~3.0.2", 83 | "karma-jasmine": "~4.0.0", 84 | "karma-jasmine-html-reporter": "^1.5.0", 85 | "ng-packagr": "^10.1.0", 86 | "protractor": "~7.0.0", 87 | "ts-node": "~5.0.1", 88 | "tslint": "~6.1.0", 89 | "typescript": "~4.0.3" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-var-keyword": true, 76 | "object-literal-sort-keys": false, 77 | "one-line": [ 78 | true, 79 | "check-open-brace", 80 | "check-catch", 81 | "check-else", 82 | "check-whitespace" 83 | ], 84 | "prefer-const": true, 85 | "quotemark": [ 86 | true, 87 | "single" 88 | ], 89 | "radix": true, 90 | "semicolon": [ 91 | true, 92 | "always" 93 | ], 94 | "triple-equals": [ 95 | true, 96 | "allow-null-check" 97 | ], 98 | "typedef-whitespace": [ 99 | true, 100 | { 101 | "call-signature": "nospace", 102 | "index-signature": "nospace", 103 | "parameter": "nospace", 104 | "property-declaration": "nospace", 105 | "variable-declaration": "nospace" 106 | } 107 | ], 108 | "unified-signatures": true, 109 | "variable-name": false, 110 | "whitespace": [ 111 | true, 112 | "check-branch", 113 | "check-decl", 114 | "check-operator", 115 | "check-separator", 116 | "check-type" 117 | ], 118 | "no-output-on-prefix": true, 119 | "no-inputs-metadata-property": true, 120 | "no-outputs-metadata-property": true, 121 | "no-host-metadata-property": true, 122 | "no-input-rename": true, 123 | "no-output-rename": true, 124 | "use-lifecycle-interface": true, 125 | "use-pipe-transform-interface": true, 126 | "component-class-suffix": true, 127 | "directive-class-suffix": true 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | 82 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/WSSecurity.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // var crypto = require('crypto'); 4 | import sha1 from 'crypto-js/sha1'; 5 | import Base64 from 'crypto-js/enc-base64'; 6 | 7 | // var passwordDigest = require('../utils').passwordDigest; 8 | import { passwordDigest } from '../utils'; 9 | 10 | var validPasswordTypes = ['PasswordDigest', 'PasswordText']; 11 | 12 | export function WSSecurity(username, password, options) { 13 | options = options || {}; 14 | this._username = username; 15 | this._password = password; 16 | //must account for backward compatibility for passwordType String param as well as object options defaults: passwordType = 'PasswordText', hasTimeStamp = true 17 | if (typeof options === 'string') { 18 | this._passwordType = options ? options : 'PasswordText'; 19 | options = {}; 20 | } else { 21 | this._passwordType = options.passwordType ? options.passwordType : 'PasswordText'; 22 | } 23 | 24 | if (validPasswordTypes.indexOf(this._passwordType) === -1) { 25 | this._passwordType = 'PasswordText'; 26 | } 27 | 28 | this._hasTimeStamp = options.hasTimeStamp || typeof options.hasTimeStamp === 'boolean' ? !!options.hasTimeStamp : true; 29 | /*jshint eqnull:true */ 30 | if (options.hasNonce != null) { 31 | this._hasNonce = !!options.hasNonce; 32 | } 33 | this._hasTokenCreated = options.hasTokenCreated || typeof options.hasTokenCreated === 'boolean' ? !!options.hasTokenCreated : true; 34 | if (options.actor != null) { 35 | this._actor = options.actor; 36 | } 37 | if (options.mustUnderstand != null) { 38 | this._mustUnderstand = !!options.mustUnderstand; 39 | } 40 | } 41 | 42 | WSSecurity.prototype.toXML = function() { 43 | // avoid dependency on date formatting libraries 44 | function getDate(d) { 45 | function pad(n) { 46 | return n < 10 ? '0' + n : n; 47 | } 48 | return d.getUTCFullYear() + '-' 49 | + pad(d.getUTCMonth() + 1) + '-' 50 | + pad(d.getUTCDate()) + 'T' 51 | + pad(d.getUTCHours()) + ':' 52 | + pad(d.getUTCMinutes()) + ':' 53 | + pad(d.getUTCSeconds()) + 'Z'; 54 | } 55 | var now = new Date(); 56 | var created = getDate(now); 57 | var timeStampXml = ''; 58 | if (this._hasTimeStamp) { 59 | var expires = getDate( new Date(now.getTime() + (1000 * 600)) ); 60 | timeStampXml = "" + 61 | ""+created+"" + 62 | ""+expires+"" + 63 | ""; 64 | } 65 | 66 | var password, nonce; 67 | if (this._hasNonce || this._passwordType !== 'PasswordText') { 68 | // nonce = base64 ( sha1 ( created + random ) ) 69 | // var nHash = crypto.createHash('sha1'); 70 | // nHash.update(created + Math.random()); 71 | // nonce = nHash.digest('base64'); 72 | nonce = Base64.stringify(sha1(created + Math.random(), '')); 73 | } 74 | if (this._passwordType === 'PasswordText') { 75 | password = "" + this._password + ""; 76 | if (nonce) { 77 | password += "" + nonce + ""; 78 | } 79 | } else { 80 | password = "" + passwordDigest(nonce, created, this._password) + "" + 81 | "" + nonce + ""; 82 | } 83 | 84 | return "" + 87 | timeStampXml + 88 | "" + 89 | "" + this._username + "" + 90 | password + 91 | (this._hasTokenCreated ? "" + created + "" : "") + 92 | "" + 93 | ""; 94 | }; 95 | 96 | // module.exports = WSSecurity; 97 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/security/WSSecurityCert.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // var fs = require('fs'); 4 | // var path = require('path'); 5 | // var ejs = require('ejs'); 6 | // var SignedXml = require('xml-crypto').SignedXml; 7 | // var uuid4 = require('uuid/v4'); 8 | 9 | import { SignedXml } from 'xml-crypto'; 10 | import uuid4 from 'uuid/v4'; 11 | 12 | let wsseSecurityHeaderTemplate; 13 | let wsseSecurityTokenTemplate; 14 | 15 | function addMinutes(date, minutes) { 16 | return new Date(date.getTime() + minutes * 60000); 17 | } 18 | 19 | function dateStringForSOAP(date) { 20 | return date.getUTCFullYear() + '-' + ('0' + (date.getUTCMonth() + 1)).slice(-2) + '-' + 21 | ('0' + date.getUTCDate()).slice(-2) + 'T' + ('0' + date.getUTCHours()).slice(-2) + ":" + 22 | ('0' + date.getUTCMinutes()).slice(-2) + ":" + ('0' + date.getUTCSeconds()).slice(-2) + "Z"; 23 | } 24 | 25 | function generateCreated() { 26 | return dateStringForSOAP(new Date()); 27 | } 28 | 29 | function generateExpires() { 30 | return dateStringForSOAP(addMinutes(new Date(), 10)); 31 | } 32 | 33 | function insertStr(src, dst, pos) { 34 | return [dst.slice(0, pos), src, dst.slice(pos)].join(''); 35 | } 36 | 37 | function generateId() { 38 | return uuid4().replace(/-/gm, ''); 39 | } 40 | 41 | export function WSSecurityCert(privatePEM, publicP12PEM, password) { 42 | this.publicP12PEM = publicP12PEM.toString().replace('-----BEGIN CERTIFICATE-----', '').replace('-----END CERTIFICATE-----', '').replace(/(\r\n|\n|\r)/gm, ''); 43 | 44 | this.signer = new SignedXml(); 45 | this.signer.signingKey = { 46 | key: privatePEM, 47 | passphrase: password 48 | }; 49 | this.x509Id = "x509-" + generateId(); 50 | 51 | var _this = this; 52 | this.signer.keyInfoProvider = {}; 53 | this.signer.keyInfoProvider.getKeyInfo = function (key) { 54 | if (!wsseSecurityTokenTemplate) { 55 | // wsseSecurityTokenTemplate = ejs.compile(fs.readFileSync(path.join(__dirname, 'templates', 'wsse-security-token.ejs')).toString()); 56 | } 57 | 58 | // return wsseSecurityTokenTemplate({ x509Id: _this.x509Id }); 59 | return ` 60 | 61 | 62 | 63 | `; 64 | }; 65 | } 66 | 67 | WSSecurityCert.prototype.postProcess = function (xml, envelopeKey) { 68 | this.created = generateCreated(); 69 | this.expires = generateExpires(); 70 | 71 | if (!wsseSecurityHeaderTemplate) { 72 | // wsseSecurityHeaderTemplate = ejs.compile(fs.readFileSync(path.join(__dirname, 'templates', 'wsse-security-header.ejs')).toString()); 73 | } 74 | 75 | // var secHeader = wsseSecurityHeaderTemplate({ 76 | // binaryToken: this.publicP12PEM, 77 | // created: this.created, 78 | // expires: this.expires, 79 | // id: this.x509Id 80 | // }); 81 | 82 | var secHeader = ` 83 | 86 | ${this.publicP12PEM} 90 | 91 | ${this.created} 92 | ${this.expires} 93 | 94 | 95 | `; 96 | 97 | var xmlWithSec = insertStr(secHeader, xml, xml.indexOf('')); 98 | 99 | var references = ["http://www.w3.org/2000/09/xmldsig#enveloped-signature", 100 | "http://www.w3.org/2001/10/xml-exc-c14n#"]; 101 | 102 | this.signer.addReference("//*[name(.)='" + envelopeKey + ":Body']", references); 103 | this.signer.addReference("//*[name(.)='wsse:Security']/*[local-name(.)='Timestamp']", references); 104 | 105 | this.signer.computeSignature(xmlWithSec); 106 | 107 | return insertStr(this.signer.getSignatureXml(), xmlWithSec, xmlWithSec.indexOf('')); 108 | }; 109 | 110 | // module.exports = WSSecurityCert; 111 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/http.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Vinay Pulim 3 | * MIT Licensed 4 | */ 5 | 6 | import * as url from 'url'; 7 | const req = null; // require('request'); 8 | import * as httpNtlm from 'httpntlm'; 9 | import { Buffer } from 'buffer'; 10 | 11 | const VERSION = '0.0.0'; //require('../package.json').version; 12 | 13 | /** 14 | * A class representing the http client 15 | * @param [options] Options object. It allows the customization of 16 | * `request` module 17 | * 18 | */ 19 | export function HttpClient(options) { 20 | options = options || {}; 21 | this._request = options.request || req; 22 | } 23 | 24 | /** 25 | * Build the HTTP request (method, uri, headers, ...) 26 | * @param rurl The resource url 27 | * @param data The payload 28 | * @param exheaders Extra http headers 29 | * @param exoptions Extra options 30 | * @returns The http request object for the `request` module 31 | */ 32 | HttpClient.prototype.buildRequest = function(rurl, data, exheaders, exoptions) { 33 | const curl = url.parse(rurl); 34 | const secure = curl.protocol === 'https:'; 35 | const host = curl.hostname; 36 | const port = parseInt(curl.port, 10); 37 | const path = [curl.pathname || '/', curl.search || '', curl.hash || ''].join(''); 38 | const method = data ? 'POST' : 'GET'; 39 | const headers = { 40 | 'User-Agent': 'node-soap/' + VERSION, 41 | 'Accept': 'text/html,application/xhtml+xml,application/xml,text/xml;q=0.9,*/*;q=0.8', 42 | 'Accept-Encoding': 'none', 43 | 'Accept-Charset': 'utf-8', 44 | 'Connection': exoptions && exoptions.forever ? 'keep-alive' : 'close', 45 | 'Host': host + (isNaN(port) ? '' : ':' + port) 46 | }; 47 | const attr = null; 48 | const header = null; 49 | const mergeOptions = ['headers']; 50 | 51 | if (typeof data === 'string') { 52 | headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); 53 | headers['Content-Type'] = 'application/x-www-form-urlencoded'; 54 | } 55 | 56 | exheaders = exheaders || {}; 57 | for (const attr in exheaders) { 58 | headers[attr] = exheaders[attr]; 59 | } 60 | 61 | const options: any = { 62 | uri: curl, 63 | method: method, 64 | headers: headers, 65 | followAllRedirects: true 66 | }; 67 | 68 | 69 | options.body = data; 70 | 71 | 72 | exoptions = exoptions || {}; 73 | for (const attr in exoptions) { 74 | if (mergeOptions.indexOf(attr) !== -1) { 75 | for (const header in exoptions[attr]) { 76 | options[attr][header] = exoptions[attr][header]; 77 | } 78 | } else { 79 | options[attr] = exoptions[attr]; 80 | } 81 | } 82 | console.log('Http request: %j', options); 83 | return options; 84 | }; 85 | 86 | /** 87 | * Handle the http response 88 | * @param The req object 89 | * @param res The res object 90 | * @param body The http body 91 | * @param The parsed body 92 | */ 93 | HttpClient.prototype.handleResponse = function(req, res, body) { 94 | console.log('Http response body: %j', body); 95 | if (typeof body === 'string') { 96 | // Remove any extra characters that appear before or after the SOAP 97 | // envelope. 98 | const match = 99 | body.replace(//, "").match(/(?:<\?[^?]*\?>[\s]*)?<([^:]*):Envelope([\S\s]*)<\/\1:Envelope>/i); 100 | if (match) { 101 | body = match[0]; 102 | } 103 | } 104 | return body; 105 | }; 106 | 107 | HttpClient.prototype.request = function(rurl, data, callback, exheaders, exoptions) { 108 | const self = this; 109 | const options = self.buildRequest(rurl, data, exheaders, exoptions); 110 | let req = null; 111 | 112 | if (exoptions !== undefined && exoptions.hasOwnProperty('ntlm')) { 113 | // // sadly when using ntlm nothing to return 114 | // // Not sure if this can be handled in a cleaner way rather than an if/else, 115 | // // will to tidy up if I get chance later, patches welcome - insanityinside 116 | options.url = rurl; 117 | httpNtlm[options.method.toLowerCase()](options, function (err, res) { 118 | if (err) { 119 | return callback(err); 120 | } 121 | // if result is stream 122 | if( typeof res.body !== 'string') { 123 | res.body = res.body.toString(); 124 | } 125 | res.body = self.handleResponse(req, res, res.body); 126 | callback(null, res, res.body); 127 | }); 128 | } else { 129 | req = self._request(options, function (err, res, body) { 130 | if (err) { 131 | return callback(err); 132 | } 133 | body = self.handleResponse(req, res, body); 134 | callback(null, res, body); 135 | }); 136 | } 137 | 138 | return req; 139 | }; 140 | 141 | HttpClient.prototype.requestStream = function(rurl, data, exheaders, exoptions) { 142 | const self = this; 143 | const options = self.buildRequest(rurl, data, exheaders, exoptions); 144 | return self._request(options); 145 | }; 146 | 147 | // module.exports = HttpClient; 148 | 149 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-soap-app": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "aot": true, 17 | "outputPath": "dist/ngx-soap-app", 18 | "index": "src/index.html", 19 | "main": "src/main.ts", 20 | "polyfills": "src/polyfills.ts", 21 | "tsConfig": "src/tsconfig.app.json", 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | { 28 | "input": "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" 29 | }, 30 | "src/styles.css" 31 | ], 32 | "scripts": [] 33 | }, 34 | "configurations": { 35 | "production": { 36 | "budgets": [ 37 | { 38 | "type": "anyComponentStyle", 39 | "maximumWarning": "6kb" 40 | } 41 | ], 42 | "fileReplacements": [ 43 | { 44 | "replace": "src/environments/environment.ts", 45 | "with": "src/environments/environment.prod.ts" 46 | } 47 | ], 48 | "optimization": true, 49 | "outputHashing": "all", 50 | "sourceMap": false, 51 | "extractCss": true, 52 | "namedChunks": false, 53 | "aot": true, 54 | "extractLicenses": true, 55 | "vendorChunk": false, 56 | "buildOptimizer": true 57 | } 58 | } 59 | }, 60 | "serve": { 61 | "builder": "@angular-devkit/build-angular:dev-server", 62 | "options": { 63 | "browserTarget": "ngx-soap-app:build" 64 | }, 65 | "configurations": { 66 | "production": { 67 | "browserTarget": "ngx-soap-app:build:production" 68 | } 69 | } 70 | }, 71 | "extract-i18n": { 72 | "builder": "@angular-devkit/build-angular:extract-i18n", 73 | "options": { 74 | "browserTarget": "ngx-soap-app:build" 75 | } 76 | }, 77 | "test": { 78 | "builder": "@angular-devkit/build-angular:karma", 79 | "options": { 80 | "main": "src/test.ts", 81 | "polyfills": "src/polyfills.ts", 82 | "tsConfig": "src/tsconfig.spec.json", 83 | "karmaConfig": "src/karma.conf.js", 84 | "styles": [ 85 | { 86 | "input": "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" 87 | }, 88 | "styles.css" 89 | ], 90 | "scripts": [], 91 | "assets": [ 92 | "src/favicon.ico", 93 | "src/assets" 94 | ] 95 | } 96 | }, 97 | "lint": { 98 | "builder": "@angular-devkit/build-angular:tslint", 99 | "options": { 100 | "tsConfig": [ 101 | "src/tsconfig.app.json", 102 | "src/tsconfig.spec.json" 103 | ], 104 | "exclude": [ 105 | "**/node_modules/**" 106 | ] 107 | } 108 | } 109 | } 110 | }, 111 | "ngx-soap-app-e2e": { 112 | "root": "e2e/", 113 | "projectType": "application", 114 | "architect": { 115 | "e2e": { 116 | "builder": "@angular-devkit/build-angular:protractor", 117 | "options": { 118 | "protractorConfig": "e2e/protractor.conf.js", 119 | "devServerTarget": "ngx-soap-app:serve" 120 | } 121 | }, 122 | "lint": { 123 | "builder": "@angular-devkit/build-angular:tslint", 124 | "options": { 125 | "tsConfig": "e2e/tsconfig.e2e.json", 126 | "exclude": [ 127 | "**/node_modules/**" 128 | ] 129 | } 130 | } 131 | } 132 | }, 133 | "ngx-soap": { 134 | "root": "projects/ngx-soap", 135 | "sourceRoot": "projects/ngx-soap/src", 136 | "projectType": "library", 137 | "prefix": "lib", 138 | "architect": { 139 | "build": { 140 | "builder": "@angular-devkit/build-ng-packagr:build", 141 | "options": { 142 | "tsConfig": "projects/ngx-soap/tsconfig.lib.json", 143 | "project": "projects/ngx-soap/ng-package.json" 144 | }, 145 | "configurations": { 146 | "production": { 147 | "project": "projects/ngx-soap/ng-package.prod.json", 148 | "tsConfig": "projects/ngx-soap/tsconfig.lib.prod.json" 149 | } 150 | } 151 | }, 152 | "test": { 153 | "builder": "@angular-devkit/build-angular:karma", 154 | "options": { 155 | "main": "projects/ngx-soap/src/test.ts", 156 | "tsConfig": "projects/ngx-soap/tsconfig.spec.json", 157 | "karmaConfig": "projects/ngx-soap/karma.conf.js" 158 | } 159 | }, 160 | "lint": { 161 | "builder": "@angular-devkit/build-angular:tslint", 162 | "options": { 163 | "tsConfig": [ 164 | "projects/ngx-soap/tsconfig.lib.json", 165 | "projects/ngx-soap/tsconfig.spec.json" 166 | ], 167 | "exclude": [ 168 | "**/node_modules/**" 169 | ] 170 | } 171 | } 172 | } 173 | } 174 | }, 175 | "defaultProject": "ngx-soap-app", 176 | "cli": { 177 | "analytics": "88b524fc-7fbb-409d-94a4-e2bece002fd1" 178 | } 179 | } -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | export interface IXmlAttribute { 6 | name: string; 7 | value: string; 8 | } 9 | 10 | export interface IWsdlBaseOptions { 11 | attributesKey?: string; 12 | valueKey?: string; 13 | xmlKey?: string; 14 | overrideRootElement?: { namespace: string; xmlnsAttributes?: IXmlAttribute[]; }; 15 | ignoredNamespaces?: boolean | string[] | { namespaces?: string[]; override?: boolean; }; 16 | ignoreBaseNameSpaces?: boolean; 17 | escapeXML?: boolean; 18 | returnFault?: boolean; 19 | handleNilAsNull?: boolean; 20 | wsdl_headers?: { [key: string]: any }; 21 | wsdl_options?: { [key: string]: any }; 22 | } 23 | 24 | export interface Definitions { 25 | descriptions: object; 26 | ignoredNamespaces: string[]; 27 | messages: WsdlMessages; 28 | portTypes: WsdlPortTypes; 29 | bindings: WsdlBindings; 30 | services: WsdlServices; 31 | schemas: WsdlSchemas; 32 | valueKey: string; 33 | xmlKey: string; 34 | xmlns: WsdlXmlns; 35 | '$targetNamespace': string; 36 | '$name': string; 37 | } 38 | 39 | 40 | export interface WsdlSchemas { 41 | [prop: string]: WsdlSchema; 42 | } 43 | export interface WsdlSchema extends XsdTypeBase { 44 | children: any[]; 45 | complexTypes?: WsdlElements; 46 | elements?: WsdlElements; 47 | includes: any[]; 48 | name: string; 49 | nsName: string; 50 | prefix: string; 51 | types?: WsdlElements; 52 | xmlns: WsdlXmlns; 53 | } 54 | export interface WsdlElements { 55 | [prop: string]: XsdElement; 56 | } 57 | export type XsdElement = XsdElementType | XsdComplexType; 58 | 59 | export interface WsdlXmlns { 60 | wsu?: string; 61 | wsp?: string; 62 | wsam?: string; 63 | soap?: string; 64 | tns?: string; 65 | xsd?: string; 66 | __tns__?: string; 67 | [prop: string]: string | void; 68 | } 69 | 70 | export interface XsdComplexType extends XsdTypeBase { 71 | children: XsdElement[] | void; 72 | name: string; 73 | nsName: string; 74 | prefix: string; 75 | '$name': string; 76 | [prop: string]: any; 77 | } 78 | 79 | export interface XsdElementType extends XsdTypeBase { 80 | children: XsdElement[] | void; 81 | name: string; 82 | nsName: string; 83 | prefix: string; 84 | targetNSAlias: string; 85 | targetNamespace: string; 86 | '$lookupType': string; 87 | '$lookupTypes': any[]; 88 | '$name': string; 89 | '$type': string; 90 | [prop: string]: any; 91 | } 92 | 93 | export interface WsdlMessages { 94 | [prop: string]: WsdlMessage; 95 | } 96 | export interface WsdlMessage extends XsdTypeBase { 97 | element: XsdElement; 98 | parts: { [prop: string]: any }; 99 | '$name': string; 100 | } 101 | 102 | export interface WsdlPortTypes { 103 | [prop: string]: WsdlPortType; 104 | } 105 | export interface WsdlPortType extends XsdTypeBase { 106 | methods: { [prop: string]: XsdElement } 107 | } 108 | 109 | export interface WsdlBindings { 110 | [prop: string]: WsdlBinding; 111 | } 112 | export interface WsdlBinding extends XsdTypeBase { 113 | methods: WsdlElements; 114 | style: string; 115 | transport: string; 116 | topElements: {[prop: string]: any}; 117 | } 118 | 119 | export interface WsdlServices { 120 | [prop: string]: WsdlService; 121 | } 122 | export interface WsdlService extends XsdTypeBase { 123 | ports: {[prop: string]: any}; 124 | } 125 | 126 | export interface XsdTypeBase { 127 | ignoredNamespaces: string[]; 128 | valueKey: string; 129 | xmlKey: string; 130 | xmlns?: WsdlXmlns, 131 | } 132 | 133 | export interface IOptions extends IWsdlBaseOptions { 134 | disableCache?: boolean; 135 | endpoint?: string; 136 | envelopeKey?: string; 137 | httpClient?: HttpClient; 138 | // request?: (options: any, callback?: (error: any, res: any, body: any) => void) => void; 139 | stream?: boolean; 140 | // wsdl options that only work for client 141 | forceSoap12Headers?: boolean; 142 | customDeserializer?: any; 143 | [key: string]: any; 144 | } 145 | 146 | export interface WSDL { 147 | constructor(definition: any, uri: string, options?: IOptions); 148 | ignoredNamespaces: string[]; 149 | ignoreBaseNameSpaces: boolean; 150 | valueKey: string; 151 | xmlKey: string; 152 | xmlnsInEnvelope: string; 153 | onReady(callback: (err:Error) => void): void; 154 | processIncludes(callback: (err:Error) => void): void; 155 | describeServices(): { [k: string]: any }; 156 | toXML(): string; 157 | xmlToObject(xml: any, callback?: (err:Error, result:any) => void): any; 158 | findSchemaObject(nsURI: string, qname: string): XsdElement | null | undefined; 159 | objectToDocumentXML(name: string, params: any, nsPrefix?: string, nsURI?: string, type?: string): any; 160 | objectToRpcXML(name: string, params: any, nsPrefix?: string, nsURI?: string, isParts?: any): string; 161 | isIgnoredNameSpace(ns: string): boolean; 162 | filterOutIgnoredNameSpace(ns: string): string; 163 | objectToXML(obj: any, name: string, nsPrefix?: any, nsURI?: string, isFirst?: boolean, xmlnsAttr?: any, schemaObject?: any, nsContext?: any): string; 164 | processAttributes(child: any, nsContext: any): string; 165 | findSchemaType(name: any, nsURI: any): any; 166 | findChildSchemaObject(parameterTypeObj: any, childName: any, backtrace?: any): any; 167 | uri: string; 168 | definitions: Definitions; 169 | } 170 | 171 | export interface Client extends EventEmitter { 172 | constructor(wsdl: WSDL, endpoint?: string, options?: IOptions); 173 | addBodyAttribute(bodyAttribute: any, name?: string, namespace?: string, xmlns?: string): void; 174 | addHttpHeader(name: string, value: any): void; 175 | addSoapHeader(soapHeader: any, name?: string, namespace?: any, xmlns?: string): number; 176 | changeSoapHeader(index: number, soapHeader: any, name?: string, namespace?: string, xmlns?: string): void; 177 | clearBodyAttributes(): void; 178 | clearHttpHeaders(): void; 179 | clearSoapHeaders(): void; 180 | describe(): any; 181 | getBodyAttributes(): any[]; 182 | getHttpHeaders(): { [k:string]: string }; 183 | getSoapHeaders(): string[]; 184 | setEndpoint(endpoint: string): void; 185 | setSOAPAction(action: string): void; 186 | setSecurity(security: ISecurity): void; 187 | wsdl: WSDL; 188 | [method: string]: ISoapMethod | WSDL | Function; 189 | call(method: string, body: any, options?: any, extraHeaders?: any): Observable; 190 | } 191 | 192 | export interface ISoapMethod { 193 | (args: any, options?: any, extraHeaders?: any): Observable; 194 | } 195 | 196 | export interface ISoapMethodResponse { 197 | err: any, 198 | header: any, 199 | responseBody: string, 200 | xml: string; 201 | result: any; 202 | } 203 | 204 | export interface ISecurity { 205 | addOptions(options: any): void; 206 | toXML(): string; 207 | } 208 | 209 | export interface BasicAuthSecurity extends ISecurity { 210 | constructor(username: string, password: string, defaults?: any); 211 | addHeaders(headers: any): void; 212 | addOptions(options: any): void; 213 | toXML(): string; 214 | } 215 | 216 | export interface BearerSecurity extends ISecurity { 217 | constructor(token: string, defaults?: any); 218 | addHeaders(headers: any): void; 219 | addOptions(options: any): void; 220 | toXML(): string; 221 | } 222 | 223 | export interface WSSecurity extends ISecurity { 224 | constructor(username: string, password: string, options?: any); 225 | addOptions(options: any): void; 226 | toXML(): string; 227 | } 228 | 229 | export interface WSSecurityCert extends ISecurity { 230 | constructor(privatePEM: any, publicP12PEM: any, password: any); 231 | addOptions(options: any): void; 232 | toXML(): string; 233 | } 234 | 235 | export interface NTLMSecurity extends ISecurity { 236 | constructor(username: string, password: string, domain: string, workstation); 237 | addHeaders(headers: any): void; 238 | addOptions(options: any): void; 239 | toXML(): string; 240 | } 241 | -------------------------------------------------------------------------------- /src/assets/calculator.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Adds two integers. This is a test WebService. ©DNE Online 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/nscontext.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export class NamespaceScope { 4 | parent: any; 5 | namespaces: any; 6 | constructor(parent: any) { 7 | if (!(this instanceof NamespaceScope)) { 8 | return new NamespaceScope(parent); 9 | } 10 | this.parent = parent; 11 | this.namespaces = {}; 12 | } 13 | 14 | getNamespaceURI = function(prefix, localOnly) { 15 | switch (prefix) { 16 | case 'xml': 17 | return 'http://www.w3.org/XML/1998/namespace'; 18 | case 'xmlns': 19 | return 'http://www.w3.org/2000/xmlns/'; 20 | default: 21 | var nsUri = this.namespaces[prefix]; 22 | /*jshint -W116 */ 23 | if (nsUri != null) { 24 | return nsUri.uri; 25 | } else if (!localOnly && this.parent) { 26 | return this.parent.getNamespaceURI(prefix); 27 | } else { 28 | return null; 29 | } 30 | } 31 | } 32 | 33 | getNamespaceMapping = function(prefix) { 34 | switch (prefix) { 35 | case 'xml': 36 | return { 37 | uri: 'http://www.w3.org/XML/1998/namespace', 38 | prefix: 'xml', 39 | declared: true 40 | }; 41 | case 'xmlns': 42 | return { 43 | uri: 'http://www.w3.org/2000/xmlns/', 44 | prefix: 'xmlns', 45 | declared: true 46 | }; 47 | default: 48 | var mapping = this.namespaces[prefix]; 49 | /*jshint -W116 */ 50 | if (mapping != null) { 51 | return mapping; 52 | } else if (this.parent) { 53 | return this.parent.getNamespaceMapping(prefix); 54 | } else { 55 | return null; 56 | } 57 | } 58 | } 59 | 60 | getPrefix = function(nsUri, localOnly) { 61 | switch (nsUri) { 62 | case 'http://www.w3.org/XML/1998/namespace': 63 | return 'xml'; 64 | case 'http://www.w3.org/2000/xmlns/': 65 | return 'xmlns'; 66 | default: 67 | for (var p in this.namespaces) { 68 | if (this.namespaces[p].uri === nsUri) { 69 | return p; 70 | } 71 | } 72 | if (!localOnly && this.parent) { 73 | return this.parent.getPrefix(nsUri); 74 | } else { 75 | return null; 76 | } 77 | } 78 | } 79 | } 80 | 81 | export class NamespaceContext { 82 | scopes: any[]; 83 | prefixCount: number; 84 | 85 | constructor() { 86 | if (!(this instanceof NamespaceContext)) { 87 | return new NamespaceContext(); 88 | } 89 | this.scopes = []; 90 | this.pushContext(); 91 | this.prefixCount = 0; 92 | } 93 | 94 | addNamespace = function(prefix, nsUri, localOnly) { 95 | if (this.getNamespaceURI(prefix, localOnly) === nsUri) { 96 | return false; 97 | } 98 | if (this.currentScope) { 99 | this.currentScope.namespaces[prefix] = { 100 | uri: nsUri, 101 | prefix: prefix, 102 | declared: false 103 | }; 104 | return true; 105 | } 106 | return false; 107 | } 108 | 109 | pushContext = function() { 110 | var scope = new NamespaceScope(this.currentScope); 111 | this.scopes.push(scope); 112 | this.currentScope = scope; 113 | return scope; 114 | } 115 | 116 | popContext = function() { 117 | var scope = this.scopes.pop(); 118 | if (scope) { 119 | this.currentScope = scope.parent; 120 | } else { 121 | this.currentScope = null; 122 | } 123 | return scope; 124 | } 125 | 126 | getNamespaceURI = function(prefix, localOnly) { 127 | return this.currentScope && this.currentScope.getNamespaceURI(prefix, localOnly); 128 | } 129 | 130 | getPrefix = function(nsUri, localOnly) { 131 | return this.currentScope && this.currentScope.getPrefix(nsUri, localOnly); 132 | } 133 | 134 | registerNamespace = function(nsUri) { 135 | var prefix = this.getPrefix(nsUri); 136 | if (prefix) { 137 | // If the namespace has already mapped to a prefix 138 | return prefix; 139 | } else { 140 | // Try to generate a unique namespace 141 | while (true) { 142 | prefix = 'ns' + (++this.prefixCount); 143 | if (!this.getNamespaceURI(prefix)) { 144 | // The prefix is not used 145 | break; 146 | } 147 | } 148 | } 149 | this.addNamespace(prefix, nsUri, true); 150 | return prefix; 151 | } 152 | 153 | declareNamespace = function(prefix, nsUri) { 154 | if (this.currentScope) { 155 | var mapping = this.currentScope.getNamespaceMapping(prefix); 156 | if (mapping && mapping.uri === nsUri && mapping.declared) { 157 | return false; 158 | } 159 | this.currentScope.namespaces[prefix] = { 160 | uri: nsUri, 161 | prefix: prefix, 162 | declared: true 163 | }; 164 | return true; 165 | } 166 | return false; 167 | } 168 | } 169 | 170 | /** 171 | * Scope for XML namespaces 172 | * @param [parent] Parent scope 173 | * 174 | */ 175 | // export function NamespaceScope(parent) { 176 | // if (!(this instanceof NamespaceScope)) { 177 | // return NamespaceScope(parent); 178 | // } 179 | // this.parent = parent; 180 | // this.namespaces = {}; 181 | // } 182 | 183 | // /** 184 | // * Namespace context that manages hierarchical scopes 185 | // * {NamespaceContext} 186 | // */ 187 | // export function NamespaceContext() { 188 | // if (!(this instanceof NamespaceContext)) { 189 | // return NamespaceContext(); 190 | // } 191 | // this.scopes = []; 192 | // this.pushContext(); 193 | // this.prefixCount = 0; 194 | // } 195 | 196 | // /** 197 | // * Look up the namespace URI by prefix 198 | // * @param prefix Namespace prefix 199 | // * @param [localOnly] Search current scope only 200 | // * Namespace URI 201 | // */ 202 | // NamespaceScope.prototype.getNamespaceURI = function(prefix, localOnly) { 203 | // switch (prefix) { 204 | // case 'xml': 205 | // return 'http://www.w3.org/XML/1998/namespace'; 206 | // case 'xmlns': 207 | // return 'http://www.w3.org/2000/xmlns/'; 208 | // default: 209 | // var nsUri = this.namespaces[prefix]; 210 | // /*jshint -W116 */ 211 | // if (nsUri != null) { 212 | // return nsUri.uri; 213 | // } else if (!localOnly && this.parent) { 214 | // return this.parent.getNamespaceURI(prefix); 215 | // } else { 216 | // return null; 217 | // } 218 | // } 219 | // }; 220 | 221 | // NamespaceScope.prototype.getNamespaceMapping = function(prefix) { 222 | // switch (prefix) { 223 | // case 'xml': 224 | // return { 225 | // uri: 'http://www.w3.org/XML/1998/namespace', 226 | // prefix: 'xml', 227 | // declared: true 228 | // }; 229 | // case 'xmlns': 230 | // return { 231 | // uri: 'http://www.w3.org/2000/xmlns/', 232 | // prefix: 'xmlns', 233 | // declared: true 234 | // }; 235 | // default: 236 | // var mapping = this.namespaces[prefix]; 237 | // /*jshint -W116 */ 238 | // if (mapping != null) { 239 | // return mapping; 240 | // } else if (this.parent) { 241 | // return this.parent.getNamespaceMapping(prefix); 242 | // } else { 243 | // return null; 244 | // } 245 | // } 246 | // }; 247 | 248 | // /** 249 | // * Look up the namespace prefix by URI 250 | // * @param nsUri Namespace URI 251 | // * @param [localOnly] Search current scope only 252 | // * Namespace prefix 253 | // */ 254 | // NamespaceScope.prototype.getPrefix = function(nsUri, localOnly) { 255 | // switch (nsUri) { 256 | // case 'http://www.w3.org/XML/1998/namespace': 257 | // return 'xml'; 258 | // case 'http://www.w3.org/2000/xmlns/': 259 | // return 'xmlns'; 260 | // default: 261 | // for (var p in this.namespaces) { 262 | // if (this.namespaces[p].uri === nsUri) { 263 | // return p; 264 | // } 265 | // } 266 | // if (!localOnly && this.parent) { 267 | // return this.parent.getPrefix(nsUri); 268 | // } else { 269 | // return null; 270 | // } 271 | // } 272 | // }; 273 | 274 | // /** 275 | // * Add a prefix/URI namespace mapping 276 | // * @param prefix Namespace prefix 277 | // * @param nsUri Namespace URI 278 | // * @param [localOnly] Search current scope only 279 | // * {boolean} true if the mapping is added or false if the mapping 280 | // * already exists 281 | // */ 282 | // NamespaceContext.prototype.addNamespace = function(prefix, nsUri, localOnly) { 283 | // if (this.getNamespaceURI(prefix, localOnly) === nsUri) { 284 | // return false; 285 | // } 286 | // if (this.currentScope) { 287 | // this.currentScope.namespaces[prefix] = { 288 | // uri: nsUri, 289 | // prefix: prefix, 290 | // declared: false 291 | // }; 292 | // return true; 293 | // } 294 | // return false; 295 | // }; 296 | 297 | // /** 298 | // * Push a scope into the context 299 | // * The current scope 300 | // */ 301 | // NamespaceContext.prototype.pushContext = function() { 302 | // var scope = NamespaceScope(this.currentScope); 303 | // this.scopes.push(scope); 304 | // this.currentScope = scope; 305 | // return scope; 306 | // }; 307 | 308 | // /** 309 | // * Pop a scope out of the context 310 | // * The removed scope 311 | // */ 312 | // NamespaceContext.prototype.popContext = function() { 313 | // var scope = this.scopes.pop(); 314 | // if (scope) { 315 | // this.currentScope = scope.parent; 316 | // } else { 317 | // this.currentScope = null; 318 | // } 319 | // return scope; 320 | // }; 321 | 322 | // /** 323 | // * Look up the namespace URI by prefix 324 | // * @param prefix Namespace prefix 325 | // * @param [localOnly] Search current scope only 326 | // * Namespace URI 327 | // */ 328 | // NamespaceContext.prototype.getNamespaceURI = function(prefix, localOnly) { 329 | // return this.currentScope && this.currentScope.getNamespaceURI(prefix, localOnly); 330 | // }; 331 | 332 | // /** 333 | // * Look up the namespace prefix by URI 334 | // * @param nsURI Namespace URI 335 | // * @param [localOnly] Search current scope only 336 | // * Namespace prefix 337 | // */ 338 | // NamespaceContext.prototype.getPrefix = function(nsUri, localOnly) { 339 | // return this.currentScope && this.currentScope.getPrefix(nsUri, localOnly); 340 | // }; 341 | 342 | // /** 343 | // * Register a namespace 344 | // * @param nsUri Namespace URI 345 | // * The matching or generated namespace prefix 346 | // */ 347 | // NamespaceContext.prototype.registerNamespace = function(nsUri) { 348 | // var prefix = this.getPrefix(nsUri); 349 | // if (prefix) { 350 | // // If the namespace has already mapped to a prefix 351 | // return prefix; 352 | // } else { 353 | // // Try to generate a unique namespace 354 | // while (true) { 355 | // prefix = 'ns' + (++this.prefixCount); 356 | // if (!this.getNamespaceURI(prefix)) { 357 | // // The prefix is not used 358 | // break; 359 | // } 360 | // } 361 | // } 362 | // this.addNamespace(prefix, nsUri, true); 363 | // return prefix; 364 | // }; 365 | 366 | // /** 367 | // * Declare a namespace prefix/uri mapping 368 | // * @param prefix Namespace prefix 369 | // * @param nsUri Namespace URI 370 | // * true if the declaration is created 371 | // */ 372 | // NamespaceContext.prototype.declareNamespace = function(prefix, nsUri) { 373 | // if (this.currentScope) { 374 | // var mapping = this.currentScope.getNamespaceMapping(prefix); 375 | // if (mapping && mapping.uri === nsUri && mapping.declared) { 376 | // return false; 377 | // } 378 | // this.currentScope.namespaces[prefix] = { 379 | // uri: nsUri, 380 | // prefix: prefix, 381 | // declared: true 382 | // }; 383 | // return true; 384 | // } 385 | // return false; 386 | // }; 387 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/_soap.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { EventEmitter } from 'events'; 4 | import * as BluebirdPromise from 'bluebird'; 5 | 6 | export interface ISoapMethod { 7 | (args: any, callback: (err: any, result: any, raw: any, soapHeader: any) => void, options?: any, extraHeaders?: any): void; 8 | } 9 | 10 | export interface ISoapServiceMethod { 11 | (args:any, callback?: (data: any) => void, headers?: any, req?: any): any; 12 | } 13 | 14 | // SOAP Fault 1.1 & 1.2 15 | export type ISoapFault = ISoapFault12 | ISoapFault11; 16 | 17 | // SOAP Fault 1.1 18 | export interface ISoapFault11 { 19 | Fault: { 20 | faultcode: number | string; 21 | faultstring: string; 22 | detail?: string; 23 | statusCode?: number; 24 | }; 25 | } 26 | 27 | // SOAP Fault 1.2 28 | // 1.2 also supports additional, optional elements: 29 | // Role, Node, Detail. Should be added when soap module implements them 30 | // https://www.w3.org/TR/soap12/#soapfault 31 | export interface ISoapFault12 { 32 | Fault: { 33 | Code: { Value: string; Subcode?: { Value: string; }; }; 34 | Reason: { Text: string; }; 35 | statusCode?: number; 36 | }; 37 | } 38 | 39 | export interface ISecurity { 40 | addOptions(options: any): void; 41 | toXML(): string; 42 | } 43 | 44 | export interface IServicePort { 45 | [methodName: string]: ISoapServiceMethod; 46 | } 47 | 48 | export interface IService { 49 | [portName: string]: IServicePort; 50 | } 51 | 52 | export interface IServices { 53 | [serviceName: string]: IService; 54 | } 55 | 56 | export interface IXmlAttribute { 57 | name: string; 58 | value: string; 59 | } 60 | 61 | export interface IWsdlBaseOptions { 62 | attributesKey?: string; 63 | valueKey?: string; 64 | xmlKey?: string; 65 | overrideRootElement?: { namespace: string; xmlnsAttributes?: IXmlAttribute[]; }; 66 | ignoredNamespaces?: boolean | string[] | { namespaces?: string[]; override?: boolean; }; 67 | ignoreBaseNameSpaces?: boolean; 68 | escapeXML?: boolean; 69 | returnFault?: boolean; 70 | handleNilAsNull?: boolean; 71 | wsdl_headers?: { [key: string]: any }; 72 | wsdl_options?: { [key: string]: any }; 73 | } 74 | 75 | export interface IOptions extends IWsdlBaseOptions { 76 | disableCache?: boolean; 77 | endpoint?: string; 78 | envelopeKey?: string; 79 | httpClient?: HttpClient; 80 | request?: (options: any, callback?: (error: any, res: any, body: any) => void) => void; 81 | stream?: boolean; 82 | // wsdl options that only work for client 83 | forceSoap12Headers?: boolean; 84 | customDeserializer?: any; 85 | [key: string]: any; 86 | } 87 | 88 | export interface IOneWayOptions { 89 | responseCode?: number; 90 | emptyBody?: boolean; 91 | } 92 | 93 | export interface IServerOptions extends IWsdlBaseOptions { 94 | path: string; 95 | services: IServices; 96 | xml?: string; 97 | uri?: string; 98 | suppressStack?: boolean; 99 | oneWay?: IOneWayOptions; 100 | [key: string]: any; 101 | } 102 | 103 | export interface Definitions { 104 | descriptions: object; 105 | ignoredNamespaces: string[]; 106 | messages: WsdlMessages; 107 | portTypes: WsdlPortTypes; 108 | bindings: WsdlBindings; 109 | services: WsdlServices; 110 | schemas: WsdlSchemas; 111 | valueKey: string; 112 | xmlKey: string; 113 | xmlns: WsdlXmlns; 114 | '$targetNamespace': string; 115 | '$name': string; 116 | } 117 | 118 | export interface XsdTypeBase { 119 | ignoredNamespaces: string[]; 120 | valueKey: string; 121 | xmlKey: string; 122 | xmlns?: WsdlXmlns, 123 | } 124 | 125 | 126 | export interface WsdlSchemas { 127 | [prop: string]: WsdlSchema; 128 | } 129 | export interface WsdlSchema extends XsdTypeBase { 130 | children: any[]; 131 | complexTypes?: WsdlElements; 132 | elements?: WsdlElements; 133 | includes: any[]; 134 | name: string; 135 | nsName: string; 136 | prefix: string; 137 | types?: WsdlElements; 138 | xmlns: WsdlXmlns; 139 | } 140 | export interface WsdlElements { 141 | [prop: string]: XsdElement; 142 | } 143 | export type XsdElement = XsdElementType | XsdComplexType; 144 | 145 | export interface WsdlXmlns { 146 | wsu?: string; 147 | wsp?: string; 148 | wsam?: string; 149 | soap?: string; 150 | tns?: string; 151 | xsd?: string; 152 | __tns__?: string; 153 | [prop: string]: string | void; 154 | } 155 | 156 | export interface XsdComplexType extends XsdTypeBase { 157 | children: XsdElement[] | void; 158 | name: string; 159 | nsName: string; 160 | prefix: string; 161 | '$name': string; 162 | [prop: string]: any; 163 | } 164 | 165 | export interface XsdElementType extends XsdTypeBase { 166 | children: XsdElement[] | void; 167 | name: string; 168 | nsName: string; 169 | prefix: string; 170 | targetNSAlias: string; 171 | targetNamespace: string; 172 | '$lookupType': string; 173 | '$lookupTypes': any[]; 174 | '$name': string; 175 | '$type': string; 176 | [prop: string]: any; 177 | } 178 | 179 | export interface WsdlMessages { 180 | [prop: string]: WsdlMessage; 181 | } 182 | export interface WsdlMessage extends XsdTypeBase { 183 | element: XsdElement; 184 | parts: { [prop: string]: any }; 185 | '$name': string; 186 | } 187 | 188 | export interface WsdlPortTypes { 189 | [prop: string]: WsdlPortType; 190 | } 191 | export interface WsdlPortType extends XsdTypeBase { 192 | methods: { [prop: string]: XsdElement } 193 | } 194 | 195 | export interface WsdlBindings { 196 | [prop: string]: WsdlBinding; 197 | } 198 | export interface WsdlBinding extends XsdTypeBase { 199 | methods: WsdlElements; 200 | style: string; 201 | transport: string; 202 | topElements: {[prop: string]: any}; 203 | } 204 | 205 | export interface WsdlServices { 206 | [prop: string]: WsdlService; 207 | } 208 | export interface WsdlService extends XsdTypeBase { 209 | ports: {[prop: string]: any}; 210 | } 211 | 212 | export class WSDL { 213 | constructor(definition: any, uri: string, options?: IOptions); 214 | ignoredNamespaces: string[]; 215 | ignoreBaseNameSpaces: boolean; 216 | valueKey: string; 217 | xmlKey: string; 218 | xmlnsInEnvelope: string; 219 | onReady(callback: (err:Error) => void): void; 220 | processIncludes(callback: (err:Error) => void): void; 221 | describeServices(): { [k: string]: any }; 222 | toXML(): string; 223 | xmlToObject(xml: any, callback?: (err:Error, result:any) => void): any; 224 | findSchemaObject(nsURI: string, qname: string): XsdElement | null | undefined; 225 | objectToDocumentXML(name: string, params: any, nsPrefix?: string, nsURI?: string, type?: string): any; 226 | objectToRpcXML(name: string, params: any, nsPrefix?: string, nsURI?: string, isParts?: any): string; 227 | isIgnoredNameSpace(ns: string): boolean; 228 | filterOutIgnoredNameSpace(ns: string): string; 229 | objectToXML(obj: any, name: string, nsPrefix?: any, nsURI?: string, isFirst?: boolean, xmlnsAttr?: any, schemaObject?: any, nsContext?: any): string; 230 | processAttributes(child: any, nsContext: any): string; 231 | findSchemaType(name: any, nsURI: any): any; 232 | findChildSchemaObject(parameterTypeObj: any, childName: any, backtrace?: any): any; 233 | uri: string; 234 | definitions: Definitions; 235 | } 236 | 237 | export class Client extends EventEmitter { 238 | constructor(wsdl: WSDL, endpoint?: string, options?: IOptions); 239 | addBodyAttribute(bodyAttribute: any, name?: string, namespace?: string, xmlns?: string): void; 240 | addHttpHeader(name: string, value: any): void; 241 | addSoapHeader(soapHeader: any, name?: string, namespace?: any, xmlns?: string): number; 242 | changeSoapHeader(index: number, soapHeader: any, name?: string, namespace?: string, xmlns?: string): void; 243 | clearBodyAttributes(): void; 244 | clearHttpHeaders(): void; 245 | clearSoapHeaders(): void; 246 | describe(): any; 247 | getBodyAttributes(): any[]; 248 | getHttpHeaders(): { [k:string]: string }; 249 | getSoapHeaders(): string[]; 250 | setEndpoint(endpoint: string): void; 251 | setSOAPAction(action: string): void; 252 | setSecurity(security: ISecurity): void; 253 | wsdl: WSDL; 254 | [method: string]: ISoapMethod | WSDL | Function; 255 | } 256 | 257 | export function createClient(url: string, callback: (err: any, client: Client) => void): void; 258 | export function createClient(url: string, options: IOptions, callback: (err: any, client: Client) => void): void; 259 | export function createClientAsync(url: string, options?: IOptions, endpoint?: string): BluebirdPromise; 260 | 261 | export class Server extends EventEmitter { 262 | constructor(server: any, path: string, services: IServices, wsdl: WSDL, options: IServerOptions); 263 | path: string; 264 | services: IServices; 265 | wsdl: WSDL; 266 | addSoapHeader(soapHeader: any, name?: string, namespace?: any, xmlns?: string): number; 267 | changeSoapHeader(index: any, soapHeader: any, name?: any, namespace?: any, xmlns?: any): void; 268 | getSoapHeaders(): string[]; 269 | clearSoapHeaders(): void; 270 | log(type: string, data: any): any; 271 | authorizeConnection(req: any): boolean; 272 | authenticate(security: ISecurity): boolean; 273 | } 274 | 275 | export function listen(server: any, path: string, service: any, wsdl: string): Server; 276 | export function listen(server: any, options: IServerOptions): Server; 277 | 278 | export class HttpClient { 279 | constructor(options?: IOptions); 280 | buildRequest(rurl: string, data: any | string, exheaders?: { [key: string]: any }, exoptions?: { [key: string]: any }): any; 281 | handleResponse(req: any, res: any, body: any | string): any | string; 282 | request(rurl: string, data: any | string, callback: (err: any, res: any, body: any | string) => void, exheaders?: { [key: string]: any }, exoptions?: { [key: string]: any }): any; 283 | requestStream(rurl: string, data: any | string, exheaders?: { [key: string]: any }, exoptions?: { [key: string]: any }): any; 284 | } 285 | 286 | export class BasicAuthSecurity implements ISecurity { 287 | constructor(username: string, password: string, defaults?: any); 288 | addHeaders(headers: any): void; 289 | addOptions(options: any): void; 290 | toXML(): string; 291 | } 292 | 293 | export class BearerSecurity implements ISecurity { 294 | constructor(token: string, defaults?: any); 295 | addHeaders(headers: any): void; 296 | addOptions(options: any): void; 297 | toXML(): string; 298 | } 299 | 300 | export class WSSecurity implements ISecurity { 301 | constructor(username: string, password: string, options?: any); 302 | addOptions(options: any): void; 303 | toXML(): string; 304 | } 305 | 306 | export class WSSecurityCert implements ISecurity { 307 | constructor(privatePEM: any, publicP12PEM: any, password: any); 308 | addOptions(options: any): void; 309 | toXML(): string; 310 | } 311 | 312 | export class ClientSSLSecurity implements ISecurity { 313 | constructor(key: string | Buffer, cert: string | Buffer, ca?: string | any[] | Buffer, defaults?: any); 314 | constructor(key: string | Buffer, cert: string | Buffer, defaults?: any); 315 | addOptions(options: any): void; 316 | toXML(): string; 317 | } 318 | 319 | export class ClientSSLSecurityPFX implements ISecurity { 320 | constructor(pfx: string | Buffer, passphrase: string, defaults?: any); 321 | constructor(pfx: string | Buffer, defaults?: any); 322 | addOptions(options: any): void; 323 | toXML(): string; 324 | } 325 | 326 | export function passwordDigest(nonce: string, created: string, password: string): string; 327 | 328 | // Below are added for backwards compatibility for previous @types/soap users. 329 | export interface Security extends ISecurity {} 330 | export interface SoapMethod extends ISoapMethod {} 331 | export interface Option extends IOptions {} 332 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/ngx-soap.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { HttpClientModule, HttpRequest } from '@angular/common/http'; 3 | import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; 4 | 5 | import { ISoapMethodResponse } from 'ngx-soap/ngx-soap'; 6 | import { NgxSoapService } from './ngx-soap.service'; 7 | import { NgxSoapModule } from './ngx-soap.module'; 8 | 9 | 10 | const PROXIED_CALCULATOR_WSDL = ` 11 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | Adds two integers. This is a test WebService. ©DNE Online 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | `; 217 | 218 | let service: NgxSoapService; 219 | let httpMock: HttpTestingController; 220 | 221 | describe('NgxSoapService', () => { 222 | beforeEach(() => { 223 | TestBed.configureTestingModule({ 224 | imports: [HttpClientModule, HttpClientTestingModule, NgxSoapModule], 225 | providers: [NgxSoapService] 226 | }); 227 | service = TestBed.inject(NgxSoapService); 228 | httpMock = TestBed.inject(HttpTestingController); 229 | }); 230 | 231 | afterEach(() => { 232 | httpMock.verify(); 233 | }); 234 | 235 | it('should be created', () => { 236 | expect(service).toBeTruthy(); 237 | }); 238 | 239 | it('should create client from calculator.wsdl', () => { 240 | service.createClient('/calculator.wsdl', { disableCache: true }) 241 | .then(client => { 242 | expect(client).toBeTruthy(); 243 | client.call('Add', { intA: 3, intB: 2 }, { exchangeId: '11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000' }) 244 | }) 245 | .catch(err => console.log('Error', err)); 246 | 247 | const req = httpMock.expectOne('/calculator.wsdl'); 248 | expect(req.request.method).toBe("GET"); 249 | req.flush(PROXIED_CALCULATOR_WSDL); 250 | }); 251 | 252 | it('should create client from calculator.wsdl', () => { 253 | service.createClient('/calculator.wsdl', { disableCache: true }) 254 | .then(client => expect(client).toBeTruthy()) 255 | .catch(err => console.log('Error', err)); 256 | 257 | let req = httpMock.expectOne('/calculator.wsdl'); 258 | req.flush(PROXIED_CALCULATOR_WSDL); 259 | httpMock.verify(); 260 | }); 261 | 262 | it('3 + 2 should be equal to 5', () => { 263 | service.createClient('/calculator.wsdl', { disableCache: true }) 264 | .then(client => { 265 | client.call('Add', { 266 | intA: 3, 267 | intB: 2 268 | }, { exchangeId: '11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000' }).subscribe((soapResponse: ISoapMethodResponse) => { 269 | expect(soapResponse.result.AddResult).toBe(5); 270 | }, err => console.error(err)); 271 | 272 | let req = httpMock.expectOne('/calculator/calculator.asmx'); 273 | let res = { 274 | headers: {}, 275 | body: `` 276 | }; 277 | req.flush('5'); 278 | httpMock.verify(); 279 | }) 280 | .catch(err => console.log('Error', err)); 281 | 282 | let req = httpMock.expectOne('/calculator.wsdl'); 283 | expect(req.request.method).toBe("GET"); 284 | req.flush(PROXIED_CALCULATOR_WSDL); 285 | }); 286 | 287 | it('should raise an error when calling a missing operation', () => { 288 | service 289 | .createClient('/calculator.wsdl', { disableCache: true }) 290 | .then(client => { 291 | const method = 'NonExistingMethod'; 292 | client 293 | .call(method, {}, { exchangeId: '11bf5b37-e0b8-42e0-8dcf-dc8c4aefc000' }) 294 | .subscribe(() => { }, err => expect(err).toBe(`Method ${method} not found`)); 295 | }); 296 | 297 | const req = httpMock.expectOne('/calculator.wsdl'); 298 | expect(req.request.method).toBe("GET"); 299 | req.flush(PROXIED_CALCULATOR_WSDL); 300 | }); 301 | }); 302 | -------------------------------------------------------------------------------- /projects/ngx-soap/src/lib/soap/client.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Vinay Pulim 3 | * MIT Licensed 4 | */ 5 | 6 | import { HttpClient, HttpResponse } from '@angular/common/http'; 7 | import * as assert from 'assert'; 8 | import { findPrefix } from './utils'; 9 | import * as _ from 'lodash'; 10 | import uuid4 from 'uuid/v4'; 11 | import { from, Observable, throwError } from 'rxjs'; 12 | import { flatMap, map } from 'rxjs/operators'; 13 | import { Multipart } from './multipart'; 14 | import { SoapAttachment } from './soapAttachment'; 15 | 16 | const nonIdentifierChars = /[^a-z$_0-9]/i; 17 | 18 | export const Client = function(wsdl, endpoint, options) { 19 | options = options || {}; 20 | this.wsdl = wsdl; 21 | this._initializeOptions(options); 22 | this._initializeServices(endpoint); 23 | this.httpClient = options.httpClient as HttpClient; 24 | const promiseOptions: any = { multiArgs: true }; 25 | if (options.overridePromiseSuffix) { 26 | promiseOptions.suffix = options.overridePromiseSuffix; 27 | } 28 | Promise.all([this, promiseOptions]); 29 | }; 30 | 31 | Client.prototype.addSoapHeader = function(soapHeader, name, namespace, xmlns) { 32 | if (!this.soapHeaders) { 33 | this.soapHeaders = []; 34 | } 35 | if (typeof soapHeader === 'object') { 36 | soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true); 37 | } 38 | return this.soapHeaders.push(soapHeader) - 1; 39 | }; 40 | 41 | Client.prototype.changeSoapHeader = function(index, soapHeader, name, namespace, xmlns) { 42 | if (!this.soapHeaders) { 43 | this.soapHeaders = []; 44 | } 45 | if (typeof soapHeader === 'object') { 46 | soapHeader = this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true); 47 | } 48 | this.soapHeaders[index] = soapHeader; 49 | }; 50 | 51 | Client.prototype.getSoapHeaders = function() { 52 | return this.soapHeaders; 53 | }; 54 | 55 | Client.prototype.clearSoapHeaders = function() { 56 | this.soapHeaders = null; 57 | }; 58 | 59 | Client.prototype.addHttpHeader = function(name, value) { 60 | if (!this.httpHeaders) { 61 | this.httpHeaders = {}; 62 | } 63 | this.httpHeaders[name] = value; 64 | }; 65 | 66 | Client.prototype.getHttpHeaders = function() { 67 | return this.httpHeaders; 68 | }; 69 | 70 | Client.prototype.clearHttpHeaders = function() { 71 | this.httpHeaders = {}; 72 | }; 73 | 74 | Client.prototype.addBodyAttribute = function(bodyAttribute, name, namespace, xmlns) { 75 | if (!this.bodyAttributes) { 76 | this.bodyAttributes = []; 77 | } 78 | if (typeof bodyAttribute === 'object') { 79 | let composition = ''; 80 | Object.getOwnPropertyNames(bodyAttribute).forEach(function(prop, idx, array) { 81 | composition += ' ' + prop + '="' + bodyAttribute[prop] + '"'; 82 | }); 83 | bodyAttribute = composition; 84 | } 85 | if (bodyAttribute.substr(0, 1) !== ' ') bodyAttribute = ' ' + bodyAttribute; 86 | this.bodyAttributes.push(bodyAttribute); 87 | }; 88 | 89 | Client.prototype.getBodyAttributes = function() { 90 | return this.bodyAttributes; 91 | }; 92 | 93 | Client.prototype.clearBodyAttributes = function() { 94 | this.bodyAttributes = null; 95 | }; 96 | 97 | Client.prototype.setEndpoint = function(endpoint) { 98 | this.endpoint = endpoint; 99 | this._initializeServices(endpoint); 100 | }; 101 | 102 | Client.prototype.describe = function() { 103 | const types = this.wsdl.definitions.types; 104 | return this.wsdl.describeServices(); 105 | }; 106 | 107 | Client.prototype.setSecurity = function(security) { 108 | this.security = security; 109 | }; 110 | 111 | Client.prototype.setSOAPAction = function(SOAPAction) { 112 | this.SOAPAction = SOAPAction; 113 | }; 114 | 115 | Client.prototype._initializeServices = function(endpoint) { 116 | const definitions = this.wsdl.definitions, 117 | services = definitions.services; 118 | for (const name in services) { 119 | this[name] = this._defineService(services[name], endpoint); 120 | } 121 | }; 122 | 123 | Client.prototype._initializeOptions = function(options) { 124 | this.streamAllowed = options.stream; 125 | this.normalizeNames = options.normalizeNames; 126 | this.wsdl.options.attributesKey = options.attributesKey || 'attributes'; 127 | this.wsdl.options.envelopeKey = options.envelopeKey || 'soap'; 128 | this.wsdl.options.preserveWhitespace = !!options.preserveWhitespace; 129 | if (options.ignoredNamespaces !== undefined) { 130 | if (options.ignoredNamespaces.override !== undefined) { 131 | if (options.ignoredNamespaces.override === true) { 132 | if (options.ignoredNamespaces.namespaces !== undefined) { 133 | this.wsdl.options.ignoredNamespaces = options.ignoredNamespaces.namespaces; 134 | } 135 | } 136 | } 137 | } 138 | if (options.overrideRootElement !== undefined) { 139 | this.wsdl.options.overrideRootElement = options.overrideRootElement; 140 | } 141 | this.wsdl.options.forceSoap12Headers = !!options.forceSoap12Headers; 142 | }; 143 | 144 | Client.prototype._defineService = function(service, endpoint) { 145 | const ports = service.ports, 146 | def = {}; 147 | for (const name in ports) { 148 | def[name] = this._definePort(ports[name], endpoint ? endpoint : ports[name].location); 149 | } 150 | return def; 151 | }; 152 | 153 | Client.prototype._definePort = function(port, endpoint) { 154 | const location = endpoint, 155 | binding = port.binding, 156 | methods = binding.methods, 157 | def = {}; 158 | for (const name in methods) { 159 | def[name] = this._defineMethod(methods[name], location); 160 | const methodName = this.normalizeNames ? name.replace(nonIdentifierChars, '_') : name; 161 | this[methodName] = def[name]; 162 | } 163 | return def; 164 | }; 165 | 166 | Client.prototype._defineMethod = function(method, location) { 167 | const self = this; 168 | let temp = null; 169 | return function(args, options, extraHeaders): Observable { 170 | return self._invoke(method, args, location, options, extraHeaders); 171 | }; 172 | }; 173 | 174 | Client.prototype._invoke = function(method, args, location, options, extraHeaders): Observable { 175 | let self = this, 176 | name = method.$name, 177 | input = method.input, 178 | output = method.output, 179 | style = method.style, 180 | defs = this.wsdl.definitions, 181 | envelopeKey = this.wsdl.options.envelopeKey, 182 | ns = defs.$targetNamespace, 183 | encoding = '', 184 | message = '', 185 | xml = null, 186 | req = null, 187 | soapAction = null, 188 | alias = findPrefix(defs.xmlns, ns), 189 | headers: any = { 190 | 'Content-Type': 'text/xml; charset=utf-8' 191 | }, 192 | xmlnsSoap = 'xmlns:' + envelopeKey + '="http://schemas.xmlsoap.org/soap/envelope/"'; 193 | 194 | if (this.wsdl.options.forceSoap12Headers) { 195 | headers['Content-Type'] = 'application/soap+xml; charset=utf-8'; 196 | xmlnsSoap = 'xmlns:' + envelopeKey + '="http://www.w3.org/2003/05/soap-envelope"'; 197 | } 198 | 199 | if (this.SOAPAction) { 200 | soapAction = this.SOAPAction; 201 | } else if (method.soapAction !== undefined && method.soapAction !== null) { 202 | soapAction = method.soapAction; 203 | } else { 204 | soapAction = (ns.lastIndexOf('/') !== ns.length - 1 ? ns + '/' : ns) + name; 205 | } 206 | 207 | if (!this.wsdl.options.forceSoap12Headers) { 208 | headers.SOAPAction = '"' + soapAction + '"'; 209 | } 210 | 211 | options = options || {}; 212 | 213 | //Add extra headers 214 | for (const header in this.httpHeaders) { 215 | headers[header] = this.httpHeaders[header]; 216 | } 217 | for (const attr in extraHeaders) { 218 | headers[attr] = extraHeaders[attr]; 219 | } 220 | 221 | // Allow the security object to add headers 222 | if (self.security && self.security.addHeaders) self.security.addHeaders(headers); 223 | if (self.security && self.security.addOptions) self.security.addOptions(options); 224 | 225 | if (style === 'rpc' && (input.parts || input.name === 'element' || args === null)) { 226 | assert.ok(!style || style === 'rpc', 'invalid message definition for document style binding'); 227 | message = self.wsdl.objectToRpcXML(name, args, alias, ns, input.name !== 'element'); 228 | method.inputSoap === 'encoded' && (encoding = 'soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" '); 229 | } else { 230 | assert.ok(!style || style === 'document', 'invalid message definition for rpc style binding'); 231 | // pass `input.$lookupType` if `input.$type` could not be found 232 | message = self.wsdl.objectToDocumentXML(input.$name, args, input.targetNSAlias, input.targetNamespace, input.$type || input.$lookupType); 233 | } 234 | xml = 235 | '' + 236 | '<' + 237 | envelopeKey + 238 | ':Envelope ' + 239 | xmlnsSoap + 240 | ' ' + 241 | 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + 242 | encoding + 243 | this.wsdl.xmlnsInEnvelope + 244 | '>' + 245 | (self.soapHeaders || self.security 246 | ? '<' + 247 | envelopeKey + 248 | ':Header>' + 249 | (self.soapHeaders ? self.soapHeaders.join('\n') : '') + 250 | (self.security && !self.security.postProcess ? self.security.toXML() : '') + 251 | '' 254 | : '') + 255 | '<' + 256 | envelopeKey + 257 | ':Body' + 258 | (self.bodyAttributes ? self.bodyAttributes.join(' ') : '') + 259 | (self.security && self.security.postProcess ? ' Id="_0"' : '') + 260 | '>' + 261 | message + 262 | '' + 265 | ''; 268 | 269 | if (self.security && self.security.postProcess) { 270 | xml = self.security.postProcess(xml, envelopeKey); 271 | } 272 | 273 | if (options && options.postProcess) { 274 | xml = options.postProcess(xml); 275 | } 276 | 277 | self.lastMessage = message; 278 | self.lastRequest = xml; 279 | self.lastEndpoint = location; 280 | 281 | const tryJSONparse = function(body) { 282 | try { 283 | return JSON.parse(body); 284 | } catch (err) { 285 | return undefined; 286 | } 287 | }; 288 | 289 | return from(SoapAttachment.fromFormFiles(options.attachments)).pipe( 290 | map((soapAttachments: SoapAttachment[]) => { 291 | if (!soapAttachments.length) { 292 | return xml; 293 | } 294 | 295 | if (options.forceMTOM || soapAttachments.length > 0) { 296 | const start = uuid4(); 297 | const boundry = uuid4(); 298 | let action = null; 299 | if (headers['Content-Type'].indexOf('action') > -1) { 300 | for (const ct of headers['Content-Type'].split('; ')) { 301 | if (ct.indexOf('action') > -1) { 302 | action = ct; 303 | } 304 | } 305 | } 306 | 307 | headers['Content-Type'] = 308 | 'multipart/related; type="application/xop+xml"; start="<' + start + '>"; start-info="text/xml"; boundary="' + boundry + '"'; 309 | if (action) { 310 | headers['Content-Type'] = headers['Content-Type'] + '; ' + action; 311 | } 312 | 313 | const multipart: any[] = [ 314 | { 315 | 'Content-Type': 'application/xop+xml; charset=UTF-8; type="text/xml"', 316 | 'Content-ID': '<' + start + '>', 317 | body: xml 318 | } 319 | ]; 320 | 321 | soapAttachments.forEach((attachment: SoapAttachment) => { 322 | multipart.push({ 323 | 'Content-Type': attachment.mimetype + ';', 324 | 'Content-Transfer-Encoding': 'binary', 325 | 'Content-ID': '<' + (attachment.contentId || attachment.name) + '>', 326 | 'Content-Disposition': 'attachment; name="' + attachment.name + '"; filename="' + attachment.name + '"', 327 | body: attachment.body 328 | }); 329 | }); 330 | 331 | return new Multipart().build(multipart, boundry); 332 | } 333 | }), 334 | flatMap((body: any) => 335 | (self.httpClient) 336 | .post(location, body, { 337 | headers: headers, 338 | responseType: 'text', 339 | observe: 'response' 340 | }) 341 | .pipe( 342 | map((response: HttpResponse) => { 343 | self.lastResponse = response.body; 344 | self.lastResponseHeaders = response && response.headers; 345 | return parseSync(response.body, response); 346 | }) 347 | ) 348 | ) 349 | ); 350 | 351 | function parseSync(body, response: HttpResponse) { 352 | let obj; 353 | try { 354 | obj = self.wsdl.xmlToObject(body); 355 | } catch (error) { 356 | // When the output element cannot be looked up in the wsdl and the body is JSON 357 | // instead of sending the error, we pass the body in the response. 358 | if (!output || !output.$lookupTypes) { 359 | // debug('Response element is not present. Unable to convert response xml to json.'); 360 | // If the response is JSON then return it as-is. 361 | const json = _.isObject(body) ? body : tryJSONparse(body); 362 | if (json) { 363 | return { err: null, response, responseBody: json, header: undefined, xml }; 364 | } 365 | } 366 | error.response = response; 367 | error.body = body; 368 | // self.emit('soapError', error, eid); 369 | throw error; 370 | } 371 | return finish(obj, body, response); 372 | } 373 | 374 | function finish(obj, responseBody, response) { 375 | let result = null; 376 | 377 | if (!output) { 378 | // one-way, no output expected 379 | return { err: null, response: null, responseBody, header: obj.Header, xml }; 380 | } 381 | 382 | // If it's not HTML and Soap Body is empty 383 | if (!obj.html && !obj.Body) { 384 | return { err: null, obj, responseBody, header: obj.Header, xml }; 385 | } 386 | 387 | if (typeof obj.Body !== 'object') { 388 | const error: any = new Error('Cannot parse response'); 389 | error.response = response; 390 | error.body = responseBody; 391 | return { err: error, obj, responseBody, header: undefined, xml }; 392 | } 393 | 394 | result = obj.Body[output.$name]; 395 | // RPC/literal response body may contain elements with added suffixes I.E. 396 | // 'Response', or 'Output', or 'Out' 397 | // This doesn't necessarily equal the ouput message name. See WSDL 1.1 Section 2.4.5 398 | if (!result) { 399 | result = obj.Body[output.$name.replace(/(?:Out(?:put)?|Response)$/, '')]; 400 | } 401 | if (!result) { 402 | ['Response', 'Out', 'Output'].forEach(function(term) { 403 | if (obj.Body.hasOwnProperty(name + term)) { 404 | return (result = obj.Body[name + term]); 405 | } 406 | }); 407 | } 408 | 409 | return { err: null, result, responseBody, header: obj.Header, xml }; 410 | } 411 | }; 412 | 413 | Client.prototype.call = function(method: string, body: any, options?: any, extraHeaders?: any): Observable { 414 | if (!this[method]) { 415 | return throwError(`Method ${method} not found`); 416 | } 417 | 418 | return (this[method]).call(this, body, options, extraHeaders); 419 | }; 420 | --------------------------------------------------------------------------------