@itentialopensource/adapter-metaswitch 1.2.2 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/adapter.js +184 -15
- package/package.json +5 -5
- package/report/adapterInfo.json +5 -5
- package/test/unit/CLAUDE.md +30 -0
- package/test/unit/adapterTestUnit.js +200 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
|
|
2
|
+
## 1.2.4 [07-01-2026]
|
|
3
|
+
|
|
4
|
+
* Changes made at 2026.07.01_14:17PM
|
|
5
|
+
|
|
6
|
+
See merge request itentialopensource/adapters/adapter-metaswitch!51
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1.2.3 [06-17-2026]
|
|
11
|
+
|
|
12
|
+
* fix: Add namespace prefix detection for OriginHost element
|
|
13
|
+
|
|
14
|
+
See merge request itentialopensource/adapters/adapter-metaswitch!48
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
2
18
|
## 1.2.2 [06-16-2026]
|
|
3
19
|
|
|
4
20
|
* Changes made at 2026.06.16_11:41AM
|
package/adapter.js
CHANGED
|
@@ -100,6 +100,7 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
100
100
|
// Exclude SOAP utility methods from workflow functions
|
|
101
101
|
myIgnore.push('wrapBodyInSoapEnvelope');
|
|
102
102
|
myIgnore.push('getSoapNamespaces');
|
|
103
|
+
myIgnore.push('collectNamespaceDeclarations');
|
|
103
104
|
myIgnore.push('buildOriginHost');
|
|
104
105
|
myIgnore.push('injectOriginHost');
|
|
105
106
|
myIgnore.push('escapeXml');
|
|
@@ -683,7 +684,7 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
683
684
|
}
|
|
684
685
|
|
|
685
686
|
// Build OriginHost with credentials from adapter properties
|
|
686
|
-
const originHostResult = this.buildOriginHost();
|
|
687
|
+
const originHostResult = this.buildOriginHost(body);
|
|
687
688
|
if (originHostResult.error) {
|
|
688
689
|
return { error: originHostResult.error };
|
|
689
690
|
}
|
|
@@ -691,8 +692,9 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
691
692
|
// Inject OriginHost into the body XML (append before any closing tags)
|
|
692
693
|
const bodyWithOriginHost = this.injectOriginHost(body, originHostResult.originHost);
|
|
693
694
|
|
|
694
|
-
//
|
|
695
|
-
|
|
695
|
+
// Collect all namespace declarations needed, scanning the body for every
|
|
696
|
+
// prefix in use so nothing is silently dropped from the final envelope
|
|
697
|
+
const namespaces = this.collectNamespaceDeclarations(bodyWithOriginHost, apiType);
|
|
696
698
|
|
|
697
699
|
// Construct SOAP envelope with minimal header (Metaswitch pattern)
|
|
698
700
|
const soapEnvelope = `<soapenv:Envelope ${namespaces}>
|
|
@@ -729,46 +731,213 @@ class Metaswitch extends AdapterBaseCl {
|
|
|
729
731
|
return `${commonNamespaces} ${apiNamespaces[apiType] || apiNamespaces.EAS}`;
|
|
730
732
|
}
|
|
731
733
|
|
|
734
|
+
/**
|
|
735
|
+
* @function collectNamespaceDeclarations
|
|
736
|
+
* @summary Scans an XML body for every namespace prefix in use and returns the
|
|
737
|
+
* complete set of xmlns declarations needed on the SOAP envelope.
|
|
738
|
+
*
|
|
739
|
+
* Resolution order for each prefix:
|
|
740
|
+
* 1. Inline declaration already present in the body (xmlns:prefix="uri")
|
|
741
|
+
* 2. API-type-aware mapping for the sh: prefix
|
|
742
|
+
* 3. Known fallback table for common XML prefixes (xsd, xsi, wsa, …)
|
|
743
|
+
* The soapenv prefix is always included regardless of body content.
|
|
744
|
+
*
|
|
745
|
+
* @param {string} body - XML body content to scan
|
|
746
|
+
* @param {string} apiType - API type (EAS, NSeries, Metaview, NWSAP)
|
|
747
|
+
* @returns {string} Space-separated xmlns declaration string
|
|
748
|
+
*/
|
|
749
|
+
// eslint-disable-next-line class-methods-use-this
|
|
750
|
+
collectNamespaceDeclarations(body, apiType = 'EAS') {
|
|
751
|
+
// -----------------------------------------------------------------------
|
|
752
|
+
// Metaswitch API-type-specific namespace table.
|
|
753
|
+
// EAS URIs are sourced from ~/Desktop/EAS_WebServices documentation
|
|
754
|
+
// (ShService.wsdl, userData.xsd, serviceData.xsd, Documentation.xsd).
|
|
755
|
+
// NSeries / Metaview / NWSAP follow the same sub-path convention.
|
|
756
|
+
//
|
|
757
|
+
// Prefix legend (all map to one of the four Metaswitch URI families):
|
|
758
|
+
// sh, impl, intf, tns1, m → base SOAP service namespace
|
|
759
|
+
// user, u → userdata sub-namespace (userData.xsd)
|
|
760
|
+
// ser, s → servicedata sub-namespace (serviceData.xsd)
|
|
761
|
+
// doc, d → documentation sub-namespace (Documentation.xsd)
|
|
762
|
+
// -----------------------------------------------------------------------
|
|
763
|
+
const apiNamespaces = {
|
|
764
|
+
EAS: {
|
|
765
|
+
sh: 'http://www.metaswitch.com/sdp/soap/sh',
|
|
766
|
+
impl: 'http://www.metaswitch.com/sdp/soap/sh',
|
|
767
|
+
intf: 'http://www.metaswitch.com/sdp/soap/sh',
|
|
768
|
+
tns1: 'http://www.metaswitch.com/sdp/soap/sh',
|
|
769
|
+
m: 'http://www.metaswitch.com/sdp/soap/sh',
|
|
770
|
+
user: 'http://www.metaswitch.com/sdp/soap/sh/userdata',
|
|
771
|
+
u: 'http://www.metaswitch.com/sdp/soap/sh/userdata',
|
|
772
|
+
ser: 'http://www.metaswitch.com/sdp/soap/sh/servicedata',
|
|
773
|
+
s: 'http://www.metaswitch.com/sdp/soap/sh/servicedata',
|
|
774
|
+
doc: 'http://www.metaswitch.com/sdp/soap/sh/documentation',
|
|
775
|
+
d: 'http://www.metaswitch.com/sdp/soap/sh/documentation'
|
|
776
|
+
},
|
|
777
|
+
NSeries: {
|
|
778
|
+
sh: 'http://www.metaswitch.com/nsrs/soap/sh',
|
|
779
|
+
impl: 'http://www.metaswitch.com/nsrs/soap/sh',
|
|
780
|
+
intf: 'http://www.metaswitch.com/nsrs/soap/sh',
|
|
781
|
+
tns1: 'http://www.metaswitch.com/nsrs/soap/sh',
|
|
782
|
+
m: 'http://www.metaswitch.com/nsrs/soap/sh',
|
|
783
|
+
user: 'http://www.metaswitch.com/nsrs/soap/sh/userdata',
|
|
784
|
+
u: 'http://www.metaswitch.com/nsrs/soap/sh/userdata',
|
|
785
|
+
ser: 'http://www.metaswitch.com/nsrs/soap/sh/servicedata',
|
|
786
|
+
s: 'http://www.metaswitch.com/nsrs/soap/sh/servicedata',
|
|
787
|
+
doc: 'http://www.metaswitch.com/nsrs/soap/sh/documentation',
|
|
788
|
+
d: 'http://www.metaswitch.com/nsrs/soap/sh/documentation'
|
|
789
|
+
},
|
|
790
|
+
Metaview: {
|
|
791
|
+
sh: 'http://www.metaswitch.com/ems/soap/sh',
|
|
792
|
+
impl: 'http://www.metaswitch.com/ems/soap/sh',
|
|
793
|
+
intf: 'http://www.metaswitch.com/ems/soap/sh',
|
|
794
|
+
tns1: 'http://www.metaswitch.com/ems/soap/sh',
|
|
795
|
+
m: 'http://www.metaswitch.com/ems/soap/sh',
|
|
796
|
+
user: 'http://www.metaswitch.com/ems/soap/sh/userdata',
|
|
797
|
+
u: 'http://www.metaswitch.com/ems/soap/sh/userdata',
|
|
798
|
+
ser: 'http://www.metaswitch.com/ems/soap/sh/servicedata',
|
|
799
|
+
s: 'http://www.metaswitch.com/ems/soap/sh/servicedata',
|
|
800
|
+
doc: 'http://www.metaswitch.com/ems/soap/sh/documentation',
|
|
801
|
+
d: 'http://www.metaswitch.com/ems/soap/sh/documentation'
|
|
802
|
+
},
|
|
803
|
+
NWSAP: {
|
|
804
|
+
sh: 'http://www.metaswitch.com/srb/soap/sh',
|
|
805
|
+
impl: 'http://www.metaswitch.com/srb/soap/sh',
|
|
806
|
+
intf: 'http://www.metaswitch.com/srb/soap/sh',
|
|
807
|
+
tns1: 'http://www.metaswitch.com/srb/soap/sh',
|
|
808
|
+
m: 'http://www.metaswitch.com/srb/soap/sh',
|
|
809
|
+
user: 'http://www.metaswitch.com/srb/soap/sh/userdata',
|
|
810
|
+
u: 'http://www.metaswitch.com/srb/soap/sh/userdata',
|
|
811
|
+
ser: 'http://www.metaswitch.com/srb/soap/sh/servicedata',
|
|
812
|
+
s: 'http://www.metaswitch.com/srb/soap/sh/servicedata',
|
|
813
|
+
doc: 'http://www.metaswitch.com/srb/soap/sh/documentation',
|
|
814
|
+
d: 'http://www.metaswitch.com/srb/soap/sh/documentation'
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
// -----------------------------------------------------------------------
|
|
819
|
+
// Standard W3C / SOAP namespaces — identical for all API types.
|
|
820
|
+
// Sources: W3C specifications, OASIS WSS, Apache SOAP.
|
|
821
|
+
// -----------------------------------------------------------------------
|
|
822
|
+
const standardNamespaces = {
|
|
823
|
+
// XML MIME types (W3C) — also always emitted on the envelope because
|
|
824
|
+
// Metaswitch declares it even when no element uses the prefix directly
|
|
825
|
+
xm: 'http://www.w3.org/2005/05/xmlmime',
|
|
826
|
+
xmime: 'http://www.w3.org/2005/05/xmlmime',
|
|
827
|
+
// XML Schema (W3C)
|
|
828
|
+
xsd: 'http://www.w3.org/2001/XMLSchema',
|
|
829
|
+
xs: 'http://www.w3.org/2001/XMLSchema',
|
|
830
|
+
// XML Schema instance (W3C)
|
|
831
|
+
xsi: 'http://www.w3.org/2001/XMLSchema-instance',
|
|
832
|
+
// WS-Addressing (W3C)
|
|
833
|
+
wsa: 'http://www.w3.org/2005/08/addressing',
|
|
834
|
+
// WS-Security utility (OASIS)
|
|
835
|
+
wsu: 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd',
|
|
836
|
+
// WSDL / SOAP binding
|
|
837
|
+
wsdl: 'http://schemas.xmlsoap.org/wsdl/',
|
|
838
|
+
wsdlsoap: 'http://schemas.xmlsoap.org/wsdl/soap/',
|
|
839
|
+
apachesoap: 'http://xml.apache.org/xml-soap'
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
// Step 1: harvest any URIs already declared inline in the body.
|
|
843
|
+
// Inline declarations are authoritative and override every table entry.
|
|
844
|
+
const declaredInBody = {};
|
|
845
|
+
(body.match(/xmlns:(\w+)="([^"]+)"/g) || []).forEach((decl) => {
|
|
846
|
+
const [, prefix, uri] = decl.match(/xmlns:(\w+)="([^"]+)"/) || [];
|
|
847
|
+
if (prefix) {
|
|
848
|
+
declaredInBody[prefix] = uri;
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
// Step 2: collect every prefix used in element names.
|
|
853
|
+
const usedPrefixes = {};
|
|
854
|
+
(body.match(/<\/?(\w+):[A-Za-z]/g) || []).forEach((token) => {
|
|
855
|
+
const [, prefix] = token.match(/(\w+):/) || [];
|
|
856
|
+
if (prefix) {
|
|
857
|
+
usedPrefixes[prefix] = true;
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
// Resolves a prefix: inline body first, then API table, then standard table.
|
|
862
|
+
const apiNs = apiNamespaces[apiType] || apiNamespaces.EAS;
|
|
863
|
+
const resolveUri = (prefix) => declaredInBody[prefix] || apiNs[prefix] || standardNamespaces[prefix];
|
|
864
|
+
|
|
865
|
+
// Step 3: build the declaration list; emit() deduplicates.
|
|
866
|
+
const emitted = {};
|
|
867
|
+
const declarations = [];
|
|
868
|
+
const emit = (prefix) => {
|
|
869
|
+
const uri = resolveUri(prefix);
|
|
870
|
+
if (!emitted[prefix] && uri) {
|
|
871
|
+
declarations.push(`xmlns:${prefix}="${uri}"`);
|
|
872
|
+
emitted[prefix] = true;
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
// Always-present: soapenv (structural envelope requirement).
|
|
877
|
+
declarations.push('xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"');
|
|
878
|
+
emitted.soapenv = true;
|
|
879
|
+
|
|
880
|
+
// Emit every prefix the body actually uses (element names + inline declarations).
|
|
881
|
+
const allBodyPrefixes = { ...usedPrefixes };
|
|
882
|
+
Object.keys(declaredInBody).forEach((p) => {
|
|
883
|
+
allBodyPrefixes[p] = true;
|
|
884
|
+
});
|
|
885
|
+
Object.keys(allBodyPrefixes).forEach(emit);
|
|
886
|
+
|
|
887
|
+
// Always-present: xm — Metaswitch includes it on every envelope even when
|
|
888
|
+
// no element in the body uses the prefix directly (e.g. for attachment MIME types).
|
|
889
|
+
emit('xm');
|
|
890
|
+
|
|
891
|
+
return declarations.join(' ');
|
|
892
|
+
}
|
|
893
|
+
|
|
732
894
|
/**
|
|
733
895
|
* @function buildOriginHost
|
|
734
896
|
* @summary Builds OriginHost XML element with credentials from adapter properties
|
|
735
|
-
* Following Metaswitch pattern:
|
|
897
|
+
* Following Metaswitch pattern: host?clientVersion=X.X&adminName=XXX&password=YYY
|
|
736
898
|
*
|
|
899
|
+
* @param {string} body - The XML body to detect namespace prefix from
|
|
737
900
|
* @returns {object} Object containing the OriginHost XML or error
|
|
738
901
|
* @returns {string} returns.originHost - The OriginHost XML element
|
|
739
902
|
* @returns {Error} returns.error - Error object if credentials are missing
|
|
740
903
|
*/
|
|
741
|
-
buildOriginHost() {
|
|
904
|
+
buildOriginHost(body) {
|
|
742
905
|
const meth = 'adapter-buildOriginHost';
|
|
743
906
|
const origin = `${this.id}-${meth}`;
|
|
744
907
|
log.trace(origin);
|
|
745
908
|
|
|
746
909
|
try {
|
|
747
910
|
// Get credentials and connection info from adapter properties
|
|
911
|
+
// Properties are at top level: this.allProps.host, this.allProps.domain, etc.
|
|
748
912
|
const auth = this.allProps.authentication || {};
|
|
749
|
-
const conn = this.allProps.properties || {};
|
|
750
913
|
|
|
751
914
|
// Validate required credentials
|
|
752
915
|
if (!auth.username || !auth.password) {
|
|
916
|
+
log.error(`${origin}: Missing authentication credentials - username: ${!!auth.username}, password: ${!!auth.password}`);
|
|
753
917
|
return { error: new Error('Missing username or password in adapter authentication configuration') };
|
|
754
918
|
}
|
|
755
919
|
|
|
756
|
-
// Build OriginHost value following Metaswitch
|
|
757
|
-
// Format:
|
|
758
|
-
const
|
|
759
|
-
const
|
|
760
|
-
|
|
920
|
+
// Build OriginHost value following working Metaswitch pattern
|
|
921
|
+
// Format: host?clientVersion=X.X&adminName=XXX&password=YYY&ignoreSequenceNumber=true
|
|
922
|
+
const host = this.allProps.host || 'server';
|
|
923
|
+
const clientVersion = this.allProps.clientVersion || '1.0';
|
|
924
|
+
|
|
925
|
+
log.debug(`${origin}: Using host=${host}, clientVersion=${clientVersion}, username=${auth.username}`);
|
|
761
926
|
|
|
762
927
|
// URL-encode credentials to handle special characters
|
|
763
928
|
const adminName = encodeURIComponent(auth.username);
|
|
764
929
|
const password = encodeURIComponent(auth.password);
|
|
765
930
|
|
|
766
|
-
const originHostValue = `${
|
|
931
|
+
const originHostValue = `${host}?clientVersion=${clientVersion}&adminName=${adminName}&password=${password}&ignoreSequenceNumber=true`;
|
|
932
|
+
|
|
933
|
+
// Detect namespace prefix from body (e.g., sh:ShPull -> use sh:OriginHost)
|
|
934
|
+
const nsMatch = body.match(/<(\w+):(ShPull|ShUpdate|ShSubs|ShNotif)/);
|
|
935
|
+
const nsPrefix = nsMatch ? `${nsMatch[1]}:` : '';
|
|
767
936
|
|
|
768
|
-
// Return as XML element
|
|
769
|
-
const originHostXml =
|
|
937
|
+
// Return as XML element with matching namespace prefix
|
|
938
|
+
const originHostXml = `<${nsPrefix}OriginHost>${this.escapeXml(originHostValue)}</${nsPrefix}OriginHost>`;
|
|
770
939
|
|
|
771
|
-
log.debug(`${origin}: Built OriginHost for ${
|
|
940
|
+
log.debug(`${origin}: Built OriginHost for ${host} with user ${auth.username} (namespace: ${nsPrefix || 'none'})`);
|
|
772
941
|
|
|
773
942
|
return { originHost: originHostXml };
|
|
774
943
|
} catch (ex) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itentialopensource/adapter-metaswitch",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "This adapter integrates with system described as: Metaswitch.",
|
|
5
5
|
"main": "adapter.js",
|
|
6
6
|
"wizardVersion": "3.12.1",
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
"author": "Itential",
|
|
47
47
|
"homepage": "https://gitlab.com/itentialopensource/adapters/adapter-metaswitch#readme",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@itentialopensource/adapter-utils": "6.1.
|
|
49
|
+
"@itentialopensource/adapter-utils": "6.1.23",
|
|
50
50
|
"acorn": "8.16.0",
|
|
51
51
|
"ajv": "8.20.0",
|
|
52
|
-
"axios": "1.
|
|
52
|
+
"axios": "1.18.1",
|
|
53
53
|
"commander": "11.1.0",
|
|
54
|
-
"fs-extra": "11.3.
|
|
54
|
+
"fs-extra": "11.3.6",
|
|
55
55
|
"json-query": "2.2.2",
|
|
56
|
-
"mocha": "12.0.0-
|
|
56
|
+
"mocha": "12.0.0-rc.1",
|
|
57
57
|
"mocha-param": "2.0.1",
|
|
58
58
|
"mongodb": "4.17.2",
|
|
59
59
|
"ping": "0.4.4",
|
package/report/adapterInfo.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.2.
|
|
2
|
+
"version": "1.2.1",
|
|
3
3
|
"configLines": 4722,
|
|
4
4
|
"scriptLines": 2523,
|
|
5
|
-
"codeLines":
|
|
6
|
-
"testLines":
|
|
7
|
-
"testCases":
|
|
8
|
-
"totalCodeLines":
|
|
5
|
+
"codeLines": 2919,
|
|
6
|
+
"testLines": 4606,
|
|
7
|
+
"testCases": 211,
|
|
8
|
+
"totalCodeLines": 10048,
|
|
9
9
|
"wfTasks": 29
|
|
10
10
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<claude-mem-context>
|
|
2
|
+
# Recent Activity
|
|
3
|
+
|
|
4
|
+
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
+
|
|
6
|
+
### Jun 4, 2026
|
|
7
|
+
|
|
8
|
+
| ID | Time | T | Title | Read |
|
|
9
|
+
|----|------|---|-------|------|
|
|
10
|
+
| #2327 | 12:06 PM | 🔵 | Entity Method Implementation Locations Identified | ~619 |
|
|
11
|
+
|
|
12
|
+
### Jun 11, 2026
|
|
13
|
+
|
|
14
|
+
| ID | Time | T | Title | Read |
|
|
15
|
+
|----|------|---|-------|------|
|
|
16
|
+
| #2411 | 9:53 AM | ✅ | Replaced WS-Security Tests with OriginHost Builder and Injector Tests | ~614 |
|
|
17
|
+
| #2410 | " | 🔄 | Updated getSoapNamespaces Test to Verify WS-Security Removal | ~403 |
|
|
18
|
+
| #2409 | " | ✅ | Updated Unit Tests for OriginHost Authentication Pattern | ~535 |
|
|
19
|
+
| #2408 | 9:52 AM | 🔄 | Updated Unit Test for OriginHost Authentication Pattern | ~176 |
|
|
20
|
+
|
|
21
|
+
### Jun 15, 2026
|
|
22
|
+
|
|
23
|
+
| ID | Time | T | Title | Read |
|
|
24
|
+
|----|------|---|-------|------|
|
|
25
|
+
| #2447 | 11:17 AM | 🔴 | OriginHost format corrected to match working implementation | ~334 |
|
|
26
|
+
| #2443 | 10:57 AM | 🟣 | Authentication Refactoring Work Completed with All Tests Passing | ~341 |
|
|
27
|
+
| #2440 | 10:22 AM | 🔴 | Property access and namespace detection fixes complete | ~306 |
|
|
28
|
+
| #2437 | 10:21 AM | 🔴 | Updated Test Assertion to Match Namespaced OriginHost Element | ~334 |
|
|
29
|
+
| #2429 | 10:10 AM | 🟣 | Added Namespace Prefix Detection to buildOriginHost Method | ~384 |
|
|
30
|
+
</claude-mem-context>
|
|
@@ -280,10 +280,10 @@ describe('[unit] Metaswitch Adapter Test', () => {
|
|
|
280
280
|
assert.notEqual(null, packageDotJson.dependencies);
|
|
281
281
|
assert.notEqual('', packageDotJson.dependencies);
|
|
282
282
|
assert.equal('8.20.0', packageDotJson.dependencies.ajv);
|
|
283
|
-
assert.equal('1.
|
|
283
|
+
assert.equal('1.18.1', packageDotJson.dependencies.axios);
|
|
284
284
|
assert.equal('11.1.0', packageDotJson.dependencies.commander);
|
|
285
|
-
assert.equal('11.3.
|
|
286
|
-
assert.equal('12.0.0-
|
|
285
|
+
assert.equal('11.3.6', packageDotJson.dependencies['fs-extra']);
|
|
286
|
+
assert.equal('12.0.0-rc.1', packageDotJson.dependencies.mocha);
|
|
287
287
|
assert.equal('2.0.1', packageDotJson.dependencies['mocha-param']);
|
|
288
288
|
assert.equal('0.4.4', packageDotJson.dependencies.ping);
|
|
289
289
|
assert.equal('1.4.10', packageDotJson.dependencies['readline-sync']);
|
|
@@ -1496,7 +1496,7 @@ describe('[unit] Metaswitch Adapter Test', () => {
|
|
|
1496
1496
|
assert.equal(result.payload.includes('<soapenv:Envelope'), true);
|
|
1497
1497
|
assert.equal(result.payload.includes('<soapenv:Header/>'), true);
|
|
1498
1498
|
assert.equal(result.payload.includes('<soapenv:Body>'), true);
|
|
1499
|
-
assert.equal(result.payload.includes('<OriginHost>'), true);
|
|
1499
|
+
assert.equal(result.payload.includes('<sh:OriginHost>'), true);
|
|
1500
1500
|
assert.equal(result.payload.includes('adminName='), true);
|
|
1501
1501
|
assert.equal(result.payload.includes('password='), true);
|
|
1502
1502
|
assert.equal(result.payload.includes('clientVersion='), true);
|
|
@@ -1612,8 +1612,31 @@ describe('[unit] Metaswitch Adapter Test', () => {
|
|
|
1612
1612
|
assert.equal(result.payload.includes('<wsse:Security'), false);
|
|
1613
1613
|
assert.equal(result.payload.includes('<wsse:UsernameToken>'), false);
|
|
1614
1614
|
|
|
1615
|
-
// Should have OriginHost with credentials
|
|
1616
|
-
assert.equal(result.payload.includes('<OriginHost>'), true);
|
|
1615
|
+
// Should have OriginHost with credentials and matching namespace prefix
|
|
1616
|
+
assert.equal(result.payload.includes('<sh:OriginHost>'), true);
|
|
1617
|
+
done();
|
|
1618
|
+
} catch (error) {
|
|
1619
|
+
log.error(`Test Failure: ${error}`);
|
|
1620
|
+
done(error);
|
|
1621
|
+
}
|
|
1622
|
+
}).timeout(attemptTimeout);
|
|
1623
|
+
|
|
1624
|
+
it('should include all namespace declarations from a multi-namespace payload', (done) => {
|
|
1625
|
+
try {
|
|
1626
|
+
// Payload that uses xsi and xsd prefixes in addition to sh
|
|
1627
|
+
const xmlPayload = '<sh:ShPull xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><UserIdentity xsi:type="xsd:string">12345</UserIdentity></sh:ShPull>';
|
|
1628
|
+
const result = a.wrapBodyInSoapEnvelope(xmlPayload, 'EAS');
|
|
1629
|
+
|
|
1630
|
+
assert.equal(result.error, undefined);
|
|
1631
|
+
assert.notEqual(result.payload, undefined);
|
|
1632
|
+
// Core envelope structure
|
|
1633
|
+
assert.equal(result.payload.includes('<soapenv:Envelope'), true);
|
|
1634
|
+
assert.equal(result.payload.includes('xmlns:soapenv='), true);
|
|
1635
|
+
// sh: namespace pulled from apiType
|
|
1636
|
+
assert.equal(result.payload.includes('xmlns:sh="http://www.metaswitch.com/sdp/soap/sh"'), true);
|
|
1637
|
+
// Additional namespaces from the payload must survive into the envelope
|
|
1638
|
+
assert.equal(result.payload.includes('xmlns:xsi='), true);
|
|
1639
|
+
assert.equal(result.payload.includes('xmlns:xsd='), true);
|
|
1617
1640
|
done();
|
|
1618
1641
|
} catch (error) {
|
|
1619
1642
|
log.error(`Test Failure: ${error}`);
|
|
@@ -1681,6 +1704,147 @@ describe('[unit] Metaswitch Adapter Test', () => {
|
|
|
1681
1704
|
}).timeout(attemptTimeout);
|
|
1682
1705
|
});
|
|
1683
1706
|
|
|
1707
|
+
describe('#collectNamespaceDeclarations', () => {
|
|
1708
|
+
it('should have a collectNamespaceDeclarations function', (done) => {
|
|
1709
|
+
try {
|
|
1710
|
+
assert.equal(true, typeof a.collectNamespaceDeclarations === 'function');
|
|
1711
|
+
done();
|
|
1712
|
+
} catch (error) {
|
|
1713
|
+
log.error(`Test Failure: ${error}`);
|
|
1714
|
+
done(error);
|
|
1715
|
+
}
|
|
1716
|
+
}).timeout(attemptTimeout);
|
|
1717
|
+
|
|
1718
|
+
it('should always include soapenv declaration', (done) => {
|
|
1719
|
+
try {
|
|
1720
|
+
const body = '<sh:ShPull><UserIdentity>test</UserIdentity></sh:ShPull>';
|
|
1721
|
+
const result = a.collectNamespaceDeclarations(body, 'EAS');
|
|
1722
|
+
|
|
1723
|
+
assert.equal(result.includes('xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"'), true);
|
|
1724
|
+
done();
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
log.error(`Test Failure: ${error}`);
|
|
1727
|
+
done(error);
|
|
1728
|
+
}
|
|
1729
|
+
}).timeout(attemptTimeout);
|
|
1730
|
+
|
|
1731
|
+
it('should resolve sh: namespace per apiType when used in body', (done) => {
|
|
1732
|
+
try {
|
|
1733
|
+
const body = '<sh:ShPull><UserIdentity>test</UserIdentity></sh:ShPull>';
|
|
1734
|
+
const apiTypes = [
|
|
1735
|
+
{ type: 'EAS', base: 'http://www.metaswitch.com/sdp/soap/sh' },
|
|
1736
|
+
{ type: 'NSeries', base: 'http://www.metaswitch.com/nsrs/soap/sh' },
|
|
1737
|
+
{ type: 'Metaview', base: 'http://www.metaswitch.com/ems/soap/sh' },
|
|
1738
|
+
{ type: 'NWSAP', base: 'http://www.metaswitch.com/srb/soap/sh' }
|
|
1739
|
+
];
|
|
1740
|
+
|
|
1741
|
+
apiTypes.forEach(({ type, base }) => {
|
|
1742
|
+
const result = a.collectNamespaceDeclarations(body, type);
|
|
1743
|
+
assert.equal(result.includes(`xmlns:sh="${base}"`), true);
|
|
1744
|
+
});
|
|
1745
|
+
done();
|
|
1746
|
+
} catch (error) {
|
|
1747
|
+
log.error(`Test Failure: ${error}`);
|
|
1748
|
+
done(error);
|
|
1749
|
+
}
|
|
1750
|
+
}).timeout(attemptTimeout);
|
|
1751
|
+
|
|
1752
|
+
it('should resolve user and ser from static table when detected in body', (done) => {
|
|
1753
|
+
try {
|
|
1754
|
+
// Body that uses user: and ser: element prefixes
|
|
1755
|
+
const body = '<sh:ShUpdate><user:Sh-Data><ser:MetaSphereData/></user:Sh-Data></sh:ShUpdate>';
|
|
1756
|
+
const apiTypes = [
|
|
1757
|
+
{ type: 'EAS', base: 'http://www.metaswitch.com/sdp/soap/sh' },
|
|
1758
|
+
{ type: 'NSeries', base: 'http://www.metaswitch.com/nsrs/soap/sh' },
|
|
1759
|
+
{ type: 'Metaview', base: 'http://www.metaswitch.com/ems/soap/sh' },
|
|
1760
|
+
{ type: 'NWSAP', base: 'http://www.metaswitch.com/srb/soap/sh' }
|
|
1761
|
+
];
|
|
1762
|
+
|
|
1763
|
+
apiTypes.forEach(({ type, base }) => {
|
|
1764
|
+
const result = a.collectNamespaceDeclarations(body, type);
|
|
1765
|
+
assert.equal(result.includes(`xmlns:user="${base}/userdata"`), true);
|
|
1766
|
+
assert.equal(result.includes(`xmlns:ser="${base}/servicedata"`), true);
|
|
1767
|
+
});
|
|
1768
|
+
done();
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
log.error(`Test Failure: ${error}`);
|
|
1771
|
+
done(error);
|
|
1772
|
+
}
|
|
1773
|
+
}).timeout(attemptTimeout);
|
|
1774
|
+
|
|
1775
|
+
it('should NOT include user or ser when absent from body', (done) => {
|
|
1776
|
+
try {
|
|
1777
|
+
// Minimal body with only sh: elements
|
|
1778
|
+
const body = '<sh:ShPull><UserIdentity>test</UserIdentity></sh:ShPull>';
|
|
1779
|
+
const result = a.collectNamespaceDeclarations(body, 'EAS');
|
|
1780
|
+
|
|
1781
|
+
assert.equal(result.includes('xmlns:user='), false);
|
|
1782
|
+
assert.equal(result.includes('xmlns:ser='), false);
|
|
1783
|
+
done();
|
|
1784
|
+
} catch (error) {
|
|
1785
|
+
log.error(`Test Failure: ${error}`);
|
|
1786
|
+
done(error);
|
|
1787
|
+
}
|
|
1788
|
+
}).timeout(attemptTimeout);
|
|
1789
|
+
|
|
1790
|
+
it('should always include xm even when absent from body', (done) => {
|
|
1791
|
+
try {
|
|
1792
|
+
// Minimal body — xm: never appears as an element prefix
|
|
1793
|
+
const body = '<sh:ShPull><UserIdentity>test</UserIdentity></sh:ShPull>';
|
|
1794
|
+
const result = a.collectNamespaceDeclarations(body, 'EAS');
|
|
1795
|
+
|
|
1796
|
+
assert.equal(result.includes('xmlns:xm="http://www.w3.org/2005/05/xmlmime"'), true);
|
|
1797
|
+
done();
|
|
1798
|
+
} catch (error) {
|
|
1799
|
+
log.error(`Test Failure: ${error}`);
|
|
1800
|
+
done(error);
|
|
1801
|
+
}
|
|
1802
|
+
}).timeout(attemptTimeout);
|
|
1803
|
+
|
|
1804
|
+
it('should include xsi and xsd declarations when used in body', (done) => {
|
|
1805
|
+
try {
|
|
1806
|
+
const body = '<sh:ShPull xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><UserIdentity xsi:type="xsd:string">test</UserIdentity></sh:ShPull>';
|
|
1807
|
+
const result = a.collectNamespaceDeclarations(body, 'EAS');
|
|
1808
|
+
|
|
1809
|
+
assert.equal(result.includes('xmlns:xsi='), true);
|
|
1810
|
+
assert.equal(result.includes('xmlns:xsd='), true);
|
|
1811
|
+
done();
|
|
1812
|
+
} catch (error) {
|
|
1813
|
+
log.error(`Test Failure: ${error}`);
|
|
1814
|
+
done(error);
|
|
1815
|
+
}
|
|
1816
|
+
}).timeout(attemptTimeout);
|
|
1817
|
+
|
|
1818
|
+
it('should prefer inline body declarations over fallback table', (done) => {
|
|
1819
|
+
try {
|
|
1820
|
+
// Body declares xsi with a custom URI — that should win over the known fallback
|
|
1821
|
+
const customUri = 'http://custom.example.com/schema-instance';
|
|
1822
|
+
const body = `<sh:ShPull xmlns:xsi="${customUri}"><UserIdentity xsi:type="string">test</UserIdentity></sh:ShPull>`;
|
|
1823
|
+
const result = a.collectNamespaceDeclarations(body, 'EAS');
|
|
1824
|
+
|
|
1825
|
+
assert.equal(result.includes(`xmlns:xsi="${customUri}"`), true);
|
|
1826
|
+
done();
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
log.error(`Test Failure: ${error}`);
|
|
1829
|
+
done(error);
|
|
1830
|
+
}
|
|
1831
|
+
}).timeout(attemptTimeout);
|
|
1832
|
+
|
|
1833
|
+
it('should not add declarations for unrecognised prefixes', (done) => {
|
|
1834
|
+
try {
|
|
1835
|
+
// zz: is not in the known fallback table and not declared inline
|
|
1836
|
+
const body = '<sh:ShPull><zz:Unknown>test</zz:Unknown></sh:ShPull>';
|
|
1837
|
+
const result = a.collectNamespaceDeclarations(body, 'EAS');
|
|
1838
|
+
|
|
1839
|
+
assert.equal(result.includes('xmlns:zz='), false);
|
|
1840
|
+
done();
|
|
1841
|
+
} catch (error) {
|
|
1842
|
+
log.error(`Test Failure: ${error}`);
|
|
1843
|
+
done(error);
|
|
1844
|
+
}
|
|
1845
|
+
}).timeout(attemptTimeout);
|
|
1846
|
+
});
|
|
1847
|
+
|
|
1684
1848
|
describe('#buildOriginHost', () => {
|
|
1685
1849
|
it('should have a buildOriginHost function', (done) => {
|
|
1686
1850
|
try {
|
|
@@ -1694,12 +1858,13 @@ describe('[unit] Metaswitch Adapter Test', () => {
|
|
|
1694
1858
|
|
|
1695
1859
|
it('should create OriginHost with credentials from adapter properties', (done) => {
|
|
1696
1860
|
try {
|
|
1697
|
-
const
|
|
1861
|
+
const body = '<sh:ShPull><UserIdentity>test</UserIdentity></sh:ShPull>';
|
|
1862
|
+
const result = a.buildOriginHost(body);
|
|
1698
1863
|
|
|
1699
1864
|
assert.equal(result.error, undefined);
|
|
1700
1865
|
assert.notEqual(result.originHost, undefined);
|
|
1701
|
-
assert.equal(result.originHost.includes('<OriginHost>'), true);
|
|
1702
|
-
assert.equal(result.originHost.includes('</OriginHost>'), true);
|
|
1866
|
+
assert.equal(result.originHost.includes('<sh:OriginHost>'), true);
|
|
1867
|
+
assert.equal(result.originHost.includes('</sh:OriginHost>'), true);
|
|
1703
1868
|
assert.equal(result.originHost.includes('adminName='), true);
|
|
1704
1869
|
assert.equal(result.originHost.includes('password='), true);
|
|
1705
1870
|
assert.equal(result.originHost.includes('clientVersion='), true);
|
|
@@ -1713,7 +1878,8 @@ describe('[unit] Metaswitch Adapter Test', () => {
|
|
|
1713
1878
|
|
|
1714
1879
|
it('should URL-encode credentials with special characters', (done) => {
|
|
1715
1880
|
try {
|
|
1716
|
-
const
|
|
1881
|
+
const body = '<sh:ShPull><UserIdentity>test</UserIdentity></sh:ShPull>';
|
|
1882
|
+
const result = a.buildOriginHost(body);
|
|
1717
1883
|
|
|
1718
1884
|
// If password contains &, it should be URL-encoded as %26
|
|
1719
1885
|
// This test validates URL encoding is applied
|
|
@@ -1727,6 +1893,30 @@ describe('[unit] Metaswitch Adapter Test', () => {
|
|
|
1727
1893
|
done(error);
|
|
1728
1894
|
}
|
|
1729
1895
|
}).timeout(attemptTimeout);
|
|
1896
|
+
|
|
1897
|
+
it('should detect and use namespace prefix from body', (done) => {
|
|
1898
|
+
try {
|
|
1899
|
+
const bodyWithNs = '<sh:ShPull><UserIdentity>test</UserIdentity></sh:ShPull>';
|
|
1900
|
+
const bodyWithoutNs = '<ShUpdate><UserIdentity>test</UserIdentity></ShUpdate>';
|
|
1901
|
+
|
|
1902
|
+
const resultWithNs = a.buildOriginHost(bodyWithNs);
|
|
1903
|
+
const resultWithoutNs = a.buildOriginHost(bodyWithoutNs);
|
|
1904
|
+
|
|
1905
|
+
// With namespace prefix
|
|
1906
|
+
assert.equal(resultWithNs.originHost.includes('<sh:OriginHost>'), true);
|
|
1907
|
+
assert.equal(resultWithNs.originHost.includes('</sh:OriginHost>'), true);
|
|
1908
|
+
|
|
1909
|
+
// Without namespace prefix
|
|
1910
|
+
assert.equal(resultWithoutNs.originHost.includes('<OriginHost>'), true);
|
|
1911
|
+
assert.equal(resultWithoutNs.originHost.includes('</OriginHost>'), true);
|
|
1912
|
+
assert.equal(resultWithoutNs.originHost.includes('<sh:OriginHost>'), false);
|
|
1913
|
+
|
|
1914
|
+
done();
|
|
1915
|
+
} catch (error) {
|
|
1916
|
+
log.error(`Test Failure: ${error}`);
|
|
1917
|
+
done(error);
|
|
1918
|
+
}
|
|
1919
|
+
}).timeout(attemptTimeout);
|
|
1730
1920
|
});
|
|
1731
1921
|
|
|
1732
1922
|
describe('#injectOriginHost', () => {
|