catalyst-relay 0.2.4 → 0.3.0
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/dist/index.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +107 -46
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +107 -46
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -142,6 +142,11 @@ function debugError(message, cause) {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
// src/core/utils/content.ts
|
|
146
|
+
function normalizeContent(content) {
|
|
147
|
+
return content.replace(/\s+/g, " ").trim();
|
|
148
|
+
}
|
|
149
|
+
|
|
145
150
|
// src/types/config.ts
|
|
146
151
|
import { z } from "zod";
|
|
147
152
|
var samlFormSelectorsSchema = z.object({
|
|
@@ -166,6 +171,7 @@ var clientConfigSchema = z.object({
|
|
|
166
171
|
type: z.literal("saml"),
|
|
167
172
|
username: z.string().min(1),
|
|
168
173
|
password: z.string().min(1),
|
|
174
|
+
sapUser: z.string().min(1),
|
|
169
175
|
providerConfig: samlProviderConfigSchema.optional()
|
|
170
176
|
}),
|
|
171
177
|
z.object({
|
|
@@ -239,8 +245,9 @@ function getSessionTimeout(authType) {
|
|
|
239
245
|
function extractUsername(auth) {
|
|
240
246
|
switch (auth.type) {
|
|
241
247
|
case "basic":
|
|
242
|
-
case "saml":
|
|
243
248
|
return auth.username;
|
|
249
|
+
case "saml":
|
|
250
|
+
return auth.sapUser;
|
|
244
251
|
case "sso":
|
|
245
252
|
return process.env["USERNAME"] ?? process.env["USER"] ?? "SSO_USER";
|
|
246
253
|
default: {
|
|
@@ -645,6 +652,46 @@ function extractActivationErrors(objects, xml, _extension) {
|
|
|
645
652
|
return ok(results);
|
|
646
653
|
}
|
|
647
654
|
|
|
655
|
+
// src/core/adt/discovery/packages.ts
|
|
656
|
+
async function getPackages(client, filter = "*") {
|
|
657
|
+
const params = new URLSearchParams([
|
|
658
|
+
["operation", "quickSearch"],
|
|
659
|
+
["query", filter],
|
|
660
|
+
["maxResults", "10001"],
|
|
661
|
+
["objectType", "DEVC/K"]
|
|
662
|
+
]);
|
|
663
|
+
const [response, requestErr] = await client.request({
|
|
664
|
+
method: "GET",
|
|
665
|
+
path: `/sap/bc/adt/repository/informationsystem/search?${params.toString()}`
|
|
666
|
+
});
|
|
667
|
+
if (requestErr) {
|
|
668
|
+
return err(requestErr);
|
|
669
|
+
}
|
|
670
|
+
if (!response.ok) {
|
|
671
|
+
const text2 = await response.text();
|
|
672
|
+
const errorMsg = extractError(text2);
|
|
673
|
+
return err(new Error(`Package search failed: ${errorMsg}`));
|
|
674
|
+
}
|
|
675
|
+
const text = await response.text();
|
|
676
|
+
const [doc, parseErr] = safeParseXml(text);
|
|
677
|
+
if (parseErr) {
|
|
678
|
+
return err(parseErr);
|
|
679
|
+
}
|
|
680
|
+
const packages = [];
|
|
681
|
+
const objectRefs = doc.getElementsByTagNameNS("http://www.sap.com/adt/core", "objectReference");
|
|
682
|
+
for (let i = 0; i < objectRefs.length; i++) {
|
|
683
|
+
const obj = objectRefs[i];
|
|
684
|
+
if (!obj) return err(new Error("Invalid object reference in package search results"));
|
|
685
|
+
const name = obj.getAttributeNS("http://www.sap.com/adt/core", "name") || obj.getAttribute("adtcore:name");
|
|
686
|
+
const description = obj.getAttributeNS("http://www.sap.com/adt/core", "description") || obj.getAttribute("adtcore:description");
|
|
687
|
+
if (!name) return err(new Error("Package name missing in object reference"));
|
|
688
|
+
const pkg = { name };
|
|
689
|
+
if (description) pkg.description = description;
|
|
690
|
+
packages.push(pkg);
|
|
691
|
+
}
|
|
692
|
+
return ok(packages);
|
|
693
|
+
}
|
|
694
|
+
|
|
648
695
|
// src/core/adt/discovery/tree.ts
|
|
649
696
|
async function getTree(client, query) {
|
|
650
697
|
const internalQuery = {};
|
|
@@ -762,32 +809,23 @@ function parseTreeResponse(xml) {
|
|
|
762
809
|
return ok({ nodes, packages });
|
|
763
810
|
}
|
|
764
811
|
|
|
765
|
-
// src/core/adt/discovery/packages.ts
|
|
766
|
-
async function getPackages(client) {
|
|
767
|
-
const [treeResult, treeErr] = await getTreeInternal(client, {}, "*");
|
|
768
|
-
if (treeErr) {
|
|
769
|
-
return err(treeErr);
|
|
770
|
-
}
|
|
771
|
-
return ok(treeResult.packages);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
812
|
// src/core/adt/transports/transports.ts
|
|
775
813
|
async function getTransports(client, packageName) {
|
|
776
814
|
const contentType = "application/vnd.sap.as+xml; charset=UTF-8; dataname=com.sap.adt.transport.service.checkData";
|
|
777
815
|
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
816
|
+
<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
|
|
817
|
+
<asx:values>
|
|
818
|
+
<DATA>
|
|
819
|
+
<PGMID></PGMID>
|
|
820
|
+
<OBJECT></OBJECT>
|
|
821
|
+
<OBJECTNAME></OBJECTNAME>
|
|
822
|
+
<DEVCLASS>${packageName}</DEVCLASS>
|
|
823
|
+
<SUPER_PACKAGE></SUPER_PACKAGE>
|
|
824
|
+
<OPERATION>I</OPERATION>
|
|
825
|
+
<URI>/sap/bc/adt/ddic/ddl/sources/zsnap_test4transports</URI>
|
|
826
|
+
</DATA>
|
|
827
|
+
</asx:values>
|
|
828
|
+
</asx:abap>`;
|
|
791
829
|
const [response, requestErr] = await client.request({
|
|
792
830
|
method: "POST",
|
|
793
831
|
path: "/sap/bc/adt/cts/transportchecks",
|
|
@@ -797,44 +835,33 @@ async function getTransports(client, packageName) {
|
|
|
797
835
|
},
|
|
798
836
|
body
|
|
799
837
|
});
|
|
800
|
-
if (requestErr)
|
|
801
|
-
return err(requestErr);
|
|
802
|
-
}
|
|
838
|
+
if (requestErr) return err(requestErr);
|
|
803
839
|
if (!response.ok) {
|
|
804
840
|
const text2 = await response.text();
|
|
805
841
|
const errorMsg = extractError(text2);
|
|
806
|
-
return err(new Error(`Failed to fetch transports for ${packageName}: ${errorMsg}`));
|
|
842
|
+
return err(new Error(`Failed to fetch transports for package ${packageName}: ${errorMsg}`));
|
|
807
843
|
}
|
|
808
844
|
const text = await response.text();
|
|
809
845
|
const [transports, parseErr] = extractTransports(text);
|
|
810
|
-
if (parseErr)
|
|
811
|
-
return err(parseErr);
|
|
812
|
-
}
|
|
846
|
+
if (parseErr) return err(parseErr);
|
|
813
847
|
return ok(transports);
|
|
814
848
|
}
|
|
815
849
|
function extractTransports(xml) {
|
|
816
850
|
const [doc, parseErr] = safeParseXml(xml);
|
|
817
|
-
if (parseErr)
|
|
818
|
-
return err(parseErr);
|
|
819
|
-
}
|
|
851
|
+
if (parseErr) return err(parseErr);
|
|
820
852
|
const transports = [];
|
|
821
853
|
const reqHeaders = doc.getElementsByTagName("REQ_HEADER");
|
|
822
854
|
for (let i = 0; i < reqHeaders.length; i++) {
|
|
823
855
|
const header = reqHeaders[i];
|
|
824
856
|
if (!header) continue;
|
|
825
|
-
const
|
|
826
|
-
const
|
|
827
|
-
const
|
|
828
|
-
if (!
|
|
829
|
-
const id = trkorrElement.textContent;
|
|
830
|
-
const owner = userElement.textContent;
|
|
831
|
-
const description = textElement.textContent;
|
|
832
|
-
if (!id || !owner || !description) continue;
|
|
857
|
+
const id = header.getElementsByTagName("TRKORR")[0]?.textContent;
|
|
858
|
+
const owner = header.getElementsByTagName("AS4USER")[0]?.textContent;
|
|
859
|
+
const description = header.getElementsByTagName("AS4TEXT")[0]?.textContent;
|
|
860
|
+
if (!id) continue;
|
|
833
861
|
transports.push({
|
|
834
862
|
id,
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
status: "modifiable"
|
|
863
|
+
description: description || "",
|
|
864
|
+
owner: owner || ""
|
|
838
865
|
});
|
|
839
866
|
}
|
|
840
867
|
return ok(transports);
|
|
@@ -1795,11 +1822,22 @@ var SamlAuth = class {
|
|
|
1795
1822
|
if (!config.username || !config.password) {
|
|
1796
1823
|
throw new Error("SamlAuth requires both username and password");
|
|
1797
1824
|
}
|
|
1825
|
+
if (!config.sapUser) {
|
|
1826
|
+
throw new Error("SamlAuth requires sapUser (SAP system username for object creation)");
|
|
1827
|
+
}
|
|
1798
1828
|
if (!config.baseUrl) {
|
|
1799
1829
|
throw new Error("SamlAuth requires baseUrl");
|
|
1800
1830
|
}
|
|
1801
1831
|
this.config = config;
|
|
1802
1832
|
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Get SAP system username
|
|
1835
|
+
*
|
|
1836
|
+
* Used for object creation (adtcore:responsible) instead of the SAML email.
|
|
1837
|
+
*/
|
|
1838
|
+
getSapUser() {
|
|
1839
|
+
return this.config.sapUser;
|
|
1840
|
+
}
|
|
1803
1841
|
/**
|
|
1804
1842
|
* Get auth headers for SAML
|
|
1805
1843
|
*
|
|
@@ -1863,6 +1901,7 @@ function createAuthStrategy(options) {
|
|
|
1863
1901
|
return new SamlAuth({
|
|
1864
1902
|
username: config.username,
|
|
1865
1903
|
password: config.password,
|
|
1904
|
+
sapUser: config.sapUser,
|
|
1866
1905
|
baseUrl,
|
|
1867
1906
|
...config.providerConfig && { providerConfig: config.providerConfig }
|
|
1868
1907
|
});
|
|
@@ -1988,6 +2027,9 @@ var ADTClientImpl = class {
|
|
|
1988
2027
|
if (this.agent) {
|
|
1989
2028
|
fetchOptions.dispatcher = this.agent;
|
|
1990
2029
|
}
|
|
2030
|
+
if (config.insecure) {
|
|
2031
|
+
fetchOptions.tls = { rejectUnauthorized: false };
|
|
2032
|
+
}
|
|
1991
2033
|
if (body) {
|
|
1992
2034
|
fetchOptions.body = body;
|
|
1993
2035
|
}
|
|
@@ -2052,6 +2094,13 @@ var ADTClientImpl = class {
|
|
|
2052
2094
|
return err(loginErr);
|
|
2053
2095
|
}
|
|
2054
2096
|
}
|
|
2097
|
+
if (authStrategy.type === "saml" && authStrategy.getCookies) {
|
|
2098
|
+
const cookies = authStrategy.getCookies();
|
|
2099
|
+
for (const cookie of cookies) {
|
|
2100
|
+
this.state.cookies.set(cookie.name, cookie.value);
|
|
2101
|
+
}
|
|
2102
|
+
debug(`Transferred ${cookies.length} SAML cookies to client`);
|
|
2103
|
+
}
|
|
2055
2104
|
if (authStrategy.type === "sso" && authStrategy.getCertificates) {
|
|
2056
2105
|
const certs = authStrategy.getCertificates();
|
|
2057
2106
|
if (certs) {
|
|
@@ -2128,6 +2177,18 @@ var ADTClientImpl = class {
|
|
|
2128
2177
|
results.push(result2);
|
|
2129
2178
|
continue;
|
|
2130
2179
|
}
|
|
2180
|
+
const serverContent = normalizeContent(existing.content);
|
|
2181
|
+
const localContent = normalizeContent(obj.content);
|
|
2182
|
+
if (serverContent === localContent) {
|
|
2183
|
+
const result2 = {
|
|
2184
|
+
name: obj.name,
|
|
2185
|
+
extension: obj.extension,
|
|
2186
|
+
status: "unchanged"
|
|
2187
|
+
};
|
|
2188
|
+
if (transport) result2.transport = transport;
|
|
2189
|
+
results.push(result2);
|
|
2190
|
+
continue;
|
|
2191
|
+
}
|
|
2131
2192
|
const [, updateErr] = await this.update(obj, transport);
|
|
2132
2193
|
if (updateErr) return err(updateErr);
|
|
2133
2194
|
const result = {
|
|
@@ -2158,9 +2219,9 @@ var ADTClientImpl = class {
|
|
|
2158
2219
|
return ok(void 0);
|
|
2159
2220
|
}
|
|
2160
2221
|
// --- Discovery ---
|
|
2161
|
-
async getPackages() {
|
|
2222
|
+
async getPackages(filter) {
|
|
2162
2223
|
if (!this.state.session) return err(new Error("Not logged in"));
|
|
2163
|
-
return getPackages(this.requestor);
|
|
2224
|
+
return getPackages(this.requestor, filter);
|
|
2164
2225
|
}
|
|
2165
2226
|
async getTree(query) {
|
|
2166
2227
|
if (!this.state.session) return err(new Error("Not logged in"));
|