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.d.mts
CHANGED
|
@@ -35,8 +35,12 @@ interface SamlProviderConfig {
|
|
|
35
35
|
*/
|
|
36
36
|
interface SamlAuthConfig {
|
|
37
37
|
type: 'saml';
|
|
38
|
+
/** SAML username (often an email address) - used for browser login */
|
|
38
39
|
username: string;
|
|
40
|
+
/** SAML password */
|
|
39
41
|
password: string;
|
|
42
|
+
/** SAP system username - used for object creation (adtcore:responsible) */
|
|
43
|
+
sapUser: string;
|
|
40
44
|
/** Optional custom provider configuration for non-standard login forms */
|
|
41
45
|
providerConfig?: SamlProviderConfig;
|
|
42
46
|
}
|
|
@@ -288,7 +292,6 @@ interface Transport {
|
|
|
288
292
|
id: string;
|
|
289
293
|
description: string;
|
|
290
294
|
owner: string;
|
|
291
|
-
status: 'modifiable' | 'released';
|
|
292
295
|
}
|
|
293
296
|
|
|
294
297
|
/**
|
|
@@ -477,7 +480,7 @@ interface ADTClient {
|
|
|
477
480
|
upsert(objects: ObjectContent[], packageName: string, transport?: string): AsyncResult<UpsertResult[]>;
|
|
478
481
|
activate(objects: ObjectRef[]): AsyncResult<ActivationResult[]>;
|
|
479
482
|
delete(objects: ObjectRef[], transport?: string): AsyncResult<void>;
|
|
480
|
-
getPackages(): AsyncResult<Package[]>;
|
|
483
|
+
getPackages(filter?: string): AsyncResult<Package[]>;
|
|
481
484
|
getTree(query: TreeQuery): AsyncResult<TreeNode[]>;
|
|
482
485
|
getTransports(packageName: string): AsyncResult<Transport[]>;
|
|
483
486
|
previewData(query: PreviewSQL): AsyncResult<DataFrame>;
|
package/dist/index.d.ts
CHANGED
|
@@ -35,8 +35,12 @@ interface SamlProviderConfig {
|
|
|
35
35
|
*/
|
|
36
36
|
interface SamlAuthConfig {
|
|
37
37
|
type: 'saml';
|
|
38
|
+
/** SAML username (often an email address) - used for browser login */
|
|
38
39
|
username: string;
|
|
40
|
+
/** SAML password */
|
|
39
41
|
password: string;
|
|
42
|
+
/** SAP system username - used for object creation (adtcore:responsible) */
|
|
43
|
+
sapUser: string;
|
|
40
44
|
/** Optional custom provider configuration for non-standard login forms */
|
|
41
45
|
providerConfig?: SamlProviderConfig;
|
|
42
46
|
}
|
|
@@ -288,7 +292,6 @@ interface Transport {
|
|
|
288
292
|
id: string;
|
|
289
293
|
description: string;
|
|
290
294
|
owner: string;
|
|
291
|
-
status: 'modifiable' | 'released';
|
|
292
295
|
}
|
|
293
296
|
|
|
294
297
|
/**
|
|
@@ -477,7 +480,7 @@ interface ADTClient {
|
|
|
477
480
|
upsert(objects: ObjectContent[], packageName: string, transport?: string): AsyncResult<UpsertResult[]>;
|
|
478
481
|
activate(objects: ObjectRef[]): AsyncResult<ActivationResult[]>;
|
|
479
482
|
delete(objects: ObjectRef[], transport?: string): AsyncResult<void>;
|
|
480
|
-
getPackages(): AsyncResult<Package[]>;
|
|
483
|
+
getPackages(filter?: string): AsyncResult<Package[]>;
|
|
481
484
|
getTree(query: TreeQuery): AsyncResult<TreeNode[]>;
|
|
482
485
|
getTransports(packageName: string): AsyncResult<Transport[]>;
|
|
483
486
|
previewData(query: PreviewSQL): AsyncResult<DataFrame>;
|
package/dist/index.js
CHANGED
|
@@ -174,6 +174,11 @@ function debugError(message, cause) {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
// src/core/utils/content.ts
|
|
178
|
+
function normalizeContent(content) {
|
|
179
|
+
return content.replace(/\s+/g, " ").trim();
|
|
180
|
+
}
|
|
181
|
+
|
|
177
182
|
// src/types/config.ts
|
|
178
183
|
var import_zod = require("zod");
|
|
179
184
|
var samlFormSelectorsSchema = import_zod.z.object({
|
|
@@ -198,6 +203,7 @@ var clientConfigSchema = import_zod.z.object({
|
|
|
198
203
|
type: import_zod.z.literal("saml"),
|
|
199
204
|
username: import_zod.z.string().min(1),
|
|
200
205
|
password: import_zod.z.string().min(1),
|
|
206
|
+
sapUser: import_zod.z.string().min(1),
|
|
201
207
|
providerConfig: samlProviderConfigSchema.optional()
|
|
202
208
|
}),
|
|
203
209
|
import_zod.z.object({
|
|
@@ -271,8 +277,9 @@ function getSessionTimeout(authType) {
|
|
|
271
277
|
function extractUsername(auth) {
|
|
272
278
|
switch (auth.type) {
|
|
273
279
|
case "basic":
|
|
274
|
-
case "saml":
|
|
275
280
|
return auth.username;
|
|
281
|
+
case "saml":
|
|
282
|
+
return auth.sapUser;
|
|
276
283
|
case "sso":
|
|
277
284
|
return process.env["USERNAME"] ?? process.env["USER"] ?? "SSO_USER";
|
|
278
285
|
default: {
|
|
@@ -677,6 +684,46 @@ function extractActivationErrors(objects, xml, _extension) {
|
|
|
677
684
|
return ok(results);
|
|
678
685
|
}
|
|
679
686
|
|
|
687
|
+
// src/core/adt/discovery/packages.ts
|
|
688
|
+
async function getPackages(client, filter = "*") {
|
|
689
|
+
const params = new URLSearchParams([
|
|
690
|
+
["operation", "quickSearch"],
|
|
691
|
+
["query", filter],
|
|
692
|
+
["maxResults", "10001"],
|
|
693
|
+
["objectType", "DEVC/K"]
|
|
694
|
+
]);
|
|
695
|
+
const [response, requestErr] = await client.request({
|
|
696
|
+
method: "GET",
|
|
697
|
+
path: `/sap/bc/adt/repository/informationsystem/search?${params.toString()}`
|
|
698
|
+
});
|
|
699
|
+
if (requestErr) {
|
|
700
|
+
return err(requestErr);
|
|
701
|
+
}
|
|
702
|
+
if (!response.ok) {
|
|
703
|
+
const text2 = await response.text();
|
|
704
|
+
const errorMsg = extractError(text2);
|
|
705
|
+
return err(new Error(`Package search failed: ${errorMsg}`));
|
|
706
|
+
}
|
|
707
|
+
const text = await response.text();
|
|
708
|
+
const [doc, parseErr] = safeParseXml(text);
|
|
709
|
+
if (parseErr) {
|
|
710
|
+
return err(parseErr);
|
|
711
|
+
}
|
|
712
|
+
const packages = [];
|
|
713
|
+
const objectRefs = doc.getElementsByTagNameNS("http://www.sap.com/adt/core", "objectReference");
|
|
714
|
+
for (let i = 0; i < objectRefs.length; i++) {
|
|
715
|
+
const obj = objectRefs[i];
|
|
716
|
+
if (!obj) return err(new Error("Invalid object reference in package search results"));
|
|
717
|
+
const name = obj.getAttributeNS("http://www.sap.com/adt/core", "name") || obj.getAttribute("adtcore:name");
|
|
718
|
+
const description = obj.getAttributeNS("http://www.sap.com/adt/core", "description") || obj.getAttribute("adtcore:description");
|
|
719
|
+
if (!name) return err(new Error("Package name missing in object reference"));
|
|
720
|
+
const pkg = { name };
|
|
721
|
+
if (description) pkg.description = description;
|
|
722
|
+
packages.push(pkg);
|
|
723
|
+
}
|
|
724
|
+
return ok(packages);
|
|
725
|
+
}
|
|
726
|
+
|
|
680
727
|
// src/core/adt/discovery/tree.ts
|
|
681
728
|
async function getTree(client, query) {
|
|
682
729
|
const internalQuery = {};
|
|
@@ -794,32 +841,23 @@ function parseTreeResponse(xml) {
|
|
|
794
841
|
return ok({ nodes, packages });
|
|
795
842
|
}
|
|
796
843
|
|
|
797
|
-
// src/core/adt/discovery/packages.ts
|
|
798
|
-
async function getPackages(client) {
|
|
799
|
-
const [treeResult, treeErr] = await getTreeInternal(client, {}, "*");
|
|
800
|
-
if (treeErr) {
|
|
801
|
-
return err(treeErr);
|
|
802
|
-
}
|
|
803
|
-
return ok(treeResult.packages);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
844
|
// src/core/adt/transports/transports.ts
|
|
807
845
|
async function getTransports(client, packageName) {
|
|
808
846
|
const contentType = "application/vnd.sap.as+xml; charset=UTF-8; dataname=com.sap.adt.transport.service.checkData";
|
|
809
847
|
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
848
|
+
<asx:abap version="1.0" xmlns:asx="http://www.sap.com/abapxml">
|
|
849
|
+
<asx:values>
|
|
850
|
+
<DATA>
|
|
851
|
+
<PGMID></PGMID>
|
|
852
|
+
<OBJECT></OBJECT>
|
|
853
|
+
<OBJECTNAME></OBJECTNAME>
|
|
854
|
+
<DEVCLASS>${packageName}</DEVCLASS>
|
|
855
|
+
<SUPER_PACKAGE></SUPER_PACKAGE>
|
|
856
|
+
<OPERATION>I</OPERATION>
|
|
857
|
+
<URI>/sap/bc/adt/ddic/ddl/sources/zsnap_test4transports</URI>
|
|
858
|
+
</DATA>
|
|
859
|
+
</asx:values>
|
|
860
|
+
</asx:abap>`;
|
|
823
861
|
const [response, requestErr] = await client.request({
|
|
824
862
|
method: "POST",
|
|
825
863
|
path: "/sap/bc/adt/cts/transportchecks",
|
|
@@ -829,44 +867,33 @@ async function getTransports(client, packageName) {
|
|
|
829
867
|
},
|
|
830
868
|
body
|
|
831
869
|
});
|
|
832
|
-
if (requestErr)
|
|
833
|
-
return err(requestErr);
|
|
834
|
-
}
|
|
870
|
+
if (requestErr) return err(requestErr);
|
|
835
871
|
if (!response.ok) {
|
|
836
872
|
const text2 = await response.text();
|
|
837
873
|
const errorMsg = extractError(text2);
|
|
838
|
-
return err(new Error(`Failed to fetch transports for ${packageName}: ${errorMsg}`));
|
|
874
|
+
return err(new Error(`Failed to fetch transports for package ${packageName}: ${errorMsg}`));
|
|
839
875
|
}
|
|
840
876
|
const text = await response.text();
|
|
841
877
|
const [transports, parseErr] = extractTransports(text);
|
|
842
|
-
if (parseErr)
|
|
843
|
-
return err(parseErr);
|
|
844
|
-
}
|
|
878
|
+
if (parseErr) return err(parseErr);
|
|
845
879
|
return ok(transports);
|
|
846
880
|
}
|
|
847
881
|
function extractTransports(xml) {
|
|
848
882
|
const [doc, parseErr] = safeParseXml(xml);
|
|
849
|
-
if (parseErr)
|
|
850
|
-
return err(parseErr);
|
|
851
|
-
}
|
|
883
|
+
if (parseErr) return err(parseErr);
|
|
852
884
|
const transports = [];
|
|
853
885
|
const reqHeaders = doc.getElementsByTagName("REQ_HEADER");
|
|
854
886
|
for (let i = 0; i < reqHeaders.length; i++) {
|
|
855
887
|
const header = reqHeaders[i];
|
|
856
888
|
if (!header) continue;
|
|
857
|
-
const
|
|
858
|
-
const
|
|
859
|
-
const
|
|
860
|
-
if (!
|
|
861
|
-
const id = trkorrElement.textContent;
|
|
862
|
-
const owner = userElement.textContent;
|
|
863
|
-
const description = textElement.textContent;
|
|
864
|
-
if (!id || !owner || !description) continue;
|
|
889
|
+
const id = header.getElementsByTagName("TRKORR")[0]?.textContent;
|
|
890
|
+
const owner = header.getElementsByTagName("AS4USER")[0]?.textContent;
|
|
891
|
+
const description = header.getElementsByTagName("AS4TEXT")[0]?.textContent;
|
|
892
|
+
if (!id) continue;
|
|
865
893
|
transports.push({
|
|
866
894
|
id,
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
status: "modifiable"
|
|
895
|
+
description: description || "",
|
|
896
|
+
owner: owner || ""
|
|
870
897
|
});
|
|
871
898
|
}
|
|
872
899
|
return ok(transports);
|
|
@@ -1827,11 +1854,22 @@ var SamlAuth = class {
|
|
|
1827
1854
|
if (!config.username || !config.password) {
|
|
1828
1855
|
throw new Error("SamlAuth requires both username and password");
|
|
1829
1856
|
}
|
|
1857
|
+
if (!config.sapUser) {
|
|
1858
|
+
throw new Error("SamlAuth requires sapUser (SAP system username for object creation)");
|
|
1859
|
+
}
|
|
1830
1860
|
if (!config.baseUrl) {
|
|
1831
1861
|
throw new Error("SamlAuth requires baseUrl");
|
|
1832
1862
|
}
|
|
1833
1863
|
this.config = config;
|
|
1834
1864
|
}
|
|
1865
|
+
/**
|
|
1866
|
+
* Get SAP system username
|
|
1867
|
+
*
|
|
1868
|
+
* Used for object creation (adtcore:responsible) instead of the SAML email.
|
|
1869
|
+
*/
|
|
1870
|
+
getSapUser() {
|
|
1871
|
+
return this.config.sapUser;
|
|
1872
|
+
}
|
|
1835
1873
|
/**
|
|
1836
1874
|
* Get auth headers for SAML
|
|
1837
1875
|
*
|
|
@@ -1895,6 +1933,7 @@ function createAuthStrategy(options) {
|
|
|
1895
1933
|
return new SamlAuth({
|
|
1896
1934
|
username: config.username,
|
|
1897
1935
|
password: config.password,
|
|
1936
|
+
sapUser: config.sapUser,
|
|
1898
1937
|
baseUrl,
|
|
1899
1938
|
...config.providerConfig && { providerConfig: config.providerConfig }
|
|
1900
1939
|
});
|
|
@@ -2020,6 +2059,9 @@ var ADTClientImpl = class {
|
|
|
2020
2059
|
if (this.agent) {
|
|
2021
2060
|
fetchOptions.dispatcher = this.agent;
|
|
2022
2061
|
}
|
|
2062
|
+
if (config.insecure) {
|
|
2063
|
+
fetchOptions.tls = { rejectUnauthorized: false };
|
|
2064
|
+
}
|
|
2023
2065
|
if (body) {
|
|
2024
2066
|
fetchOptions.body = body;
|
|
2025
2067
|
}
|
|
@@ -2084,6 +2126,13 @@ var ADTClientImpl = class {
|
|
|
2084
2126
|
return err(loginErr);
|
|
2085
2127
|
}
|
|
2086
2128
|
}
|
|
2129
|
+
if (authStrategy.type === "saml" && authStrategy.getCookies) {
|
|
2130
|
+
const cookies = authStrategy.getCookies();
|
|
2131
|
+
for (const cookie of cookies) {
|
|
2132
|
+
this.state.cookies.set(cookie.name, cookie.value);
|
|
2133
|
+
}
|
|
2134
|
+
debug(`Transferred ${cookies.length} SAML cookies to client`);
|
|
2135
|
+
}
|
|
2087
2136
|
if (authStrategy.type === "sso" && authStrategy.getCertificates) {
|
|
2088
2137
|
const certs = authStrategy.getCertificates();
|
|
2089
2138
|
if (certs) {
|
|
@@ -2160,6 +2209,18 @@ var ADTClientImpl = class {
|
|
|
2160
2209
|
results.push(result2);
|
|
2161
2210
|
continue;
|
|
2162
2211
|
}
|
|
2212
|
+
const serverContent = normalizeContent(existing.content);
|
|
2213
|
+
const localContent = normalizeContent(obj.content);
|
|
2214
|
+
if (serverContent === localContent) {
|
|
2215
|
+
const result2 = {
|
|
2216
|
+
name: obj.name,
|
|
2217
|
+
extension: obj.extension,
|
|
2218
|
+
status: "unchanged"
|
|
2219
|
+
};
|
|
2220
|
+
if (transport) result2.transport = transport;
|
|
2221
|
+
results.push(result2);
|
|
2222
|
+
continue;
|
|
2223
|
+
}
|
|
2163
2224
|
const [, updateErr] = await this.update(obj, transport);
|
|
2164
2225
|
if (updateErr) return err(updateErr);
|
|
2165
2226
|
const result = {
|
|
@@ -2190,9 +2251,9 @@ var ADTClientImpl = class {
|
|
|
2190
2251
|
return ok(void 0);
|
|
2191
2252
|
}
|
|
2192
2253
|
// --- Discovery ---
|
|
2193
|
-
async getPackages() {
|
|
2254
|
+
async getPackages(filter) {
|
|
2194
2255
|
if (!this.state.session) return err(new Error("Not logged in"));
|
|
2195
|
-
return getPackages(this.requestor);
|
|
2256
|
+
return getPackages(this.requestor, filter);
|
|
2196
2257
|
}
|
|
2197
2258
|
async getTree(query) {
|
|
2198
2259
|
if (!this.state.session) return err(new Error("Not logged in"));
|