arc-1 0.9.2 → 0.9.3
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/handlers/intent.d.ts +20 -0
- package/dist/handlers/intent.d.ts.map +1 -1
- package/dist/handlers/intent.js +175 -11
- package/dist/handlers/intent.js.map +1 -1
- package/dist/handlers/schemas.d.ts +5 -1
- package/dist/handlers/schemas.d.ts.map +1 -1
- package/dist/handlers/schemas.js +6 -0
- package/dist/handlers/schemas.js.map +1 -1
- package/dist/handlers/tools.d.ts.map +1 -1
- package/dist/handlers/tools.js +9 -2
- package/dist/handlers/tools.js.map +1 -1
- package/dist/server/server.d.ts +1 -1
- package/dist/server/server.js +1 -1
- package/package.json +1 -2
|
@@ -63,6 +63,26 @@ export declare function handleToolCall(client: AdtClient, config: ServerConfig,
|
|
|
63
63
|
*/
|
|
64
64
|
export declare function warnCdsReservedKeywords(source: string): string | undefined;
|
|
65
65
|
export declare function buildCreateXml(type: string, name: string, pkg: string, description: string, properties?: Record<string, unknown>): string;
|
|
66
|
+
/**
|
|
67
|
+
* Strip SAPGUI-style function-module parameter comment blocks from an FM source body.
|
|
68
|
+
*
|
|
69
|
+
* SAP rejects PUT-to-source/main with parameter comment blocks (verified live on a4h
|
|
70
|
+
* S/4HANA 2023 — issue #250):
|
|
71
|
+
* HTTP 400 / com.sap.adt.sedi / ExceptionResourceScanDuringSaveFailure
|
|
72
|
+
* "Parameter comment blocks are not allowed" (T100KEY FUNC_ADT028)
|
|
73
|
+
*
|
|
74
|
+
* The signature is metadata, not source. LLMs frequently emit the SAPGUI block out
|
|
75
|
+
* of muscle memory (every released FM ships with one). This helper strips lines whose
|
|
76
|
+
* first non-whitespace tokens are `*"` so the PUT succeeds, and reports back whether
|
|
77
|
+
* stripping occurred so the caller can append a warning to the response.
|
|
78
|
+
*
|
|
79
|
+
* Only `*"…` lines are stripped — single `*` ABAP comments and inline `"` comments
|
|
80
|
+
* are preserved. Exported for unit tests.
|
|
81
|
+
*/
|
|
82
|
+
export declare function stripFmParamCommentBlock(source: string): {
|
|
83
|
+
source: string;
|
|
84
|
+
wasStripped: boolean;
|
|
85
|
+
};
|
|
66
86
|
export declare const SLASH_TYPE_MAP: Record<string, string>;
|
|
67
87
|
/**
|
|
68
88
|
* Citation guard companion for SLASH_TYPE_MAP. Keys MUST stay key-equal to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/handlers/intent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAqBxE,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,kBAAkB,CAAC;AAmHpE,OAAO,KAAK,EAKV,gBAAgB,EACjB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAc9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKvD,2BAA2B;AAC3B,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAiBD;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAe9C,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAKnF;AAqFD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAe1F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CASzD;AAkqBD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,YAAY,EAC3B,eAAe,CAAC,EAAE,OAAO,GACxB,OAAO,CAAC,UAAU,CAAC,CA+MrB;
|
|
1
|
+
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/handlers/intent.ts"],"names":[],"mappings":"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"}
|
package/dist/handlers/intent.js
CHANGED
|
@@ -1590,12 +1590,22 @@ const SERVICEBINDING_V2_CONTENT_TYPE = 'application/vnd.sap.adt.businessservices
|
|
|
1590
1590
|
const BDEF_CONTENT_TYPE = 'application/vnd.sap.adt.blues.v1+xml';
|
|
1591
1591
|
const MESSAGECLASS_CONTENT_TYPE = 'application/vnd.sap.adt.mc.messageclass+xml';
|
|
1592
1592
|
const SKTD_V2_CONTENT_TYPE = 'application/vnd.sap.adt.sktdv2+xml';
|
|
1593
|
+
// Function group + function module content types — verified live on a4h S/4HANA 2023
|
|
1594
|
+
// (issue #250). FUGR uses the v3 group envelope; FUNC uses the unversioned fmodule envelope.
|
|
1595
|
+
const FUNCTION_GROUP_CONTENT_TYPE = 'application/vnd.sap.adt.functions.groups.v3+xml';
|
|
1596
|
+
const FUNCTION_MODULE_CONTENT_TYPE = 'application/vnd.sap.adt.functions.fmodules+xml';
|
|
1593
1597
|
function isMetadataWriteType(type) {
|
|
1594
1598
|
return type === 'DOMA' || type === 'DTEL' || type === 'MSAG' || type === 'SRVB';
|
|
1595
1599
|
}
|
|
1596
1600
|
/** Types that require a specific vendor content type for creation (not application/*) */
|
|
1597
1601
|
function needsVendorContentType(type) {
|
|
1598
|
-
return type === 'DOMA' ||
|
|
1602
|
+
return (type === 'DOMA' ||
|
|
1603
|
+
type === 'DTEL' ||
|
|
1604
|
+
type === 'BDEF' ||
|
|
1605
|
+
type === 'MSAG' ||
|
|
1606
|
+
type === 'SKTD' ||
|
|
1607
|
+
type === 'FUGR' ||
|
|
1608
|
+
type === 'FUNC');
|
|
1599
1609
|
}
|
|
1600
1610
|
/** Content type used for create POST */
|
|
1601
1611
|
function createContentTypeForType(type) {
|
|
@@ -1634,6 +1644,10 @@ function vendorContentTypeForType(type) {
|
|
|
1634
1644
|
return MESSAGECLASS_CONTENT_TYPE;
|
|
1635
1645
|
case 'SKTD':
|
|
1636
1646
|
return SKTD_V2_CONTENT_TYPE;
|
|
1647
|
+
case 'FUGR':
|
|
1648
|
+
return FUNCTION_GROUP_CONTENT_TYPE;
|
|
1649
|
+
case 'FUNC':
|
|
1650
|
+
return FUNCTION_MODULE_CONTENT_TYPE;
|
|
1637
1651
|
default:
|
|
1638
1652
|
// Wildcard lets the SAP server resolve the correct handler.
|
|
1639
1653
|
// Sending 'application/xml' causes 415 on DDL-based endpoints
|
|
@@ -1684,6 +1698,9 @@ function getMetadataWriteProperties(input) {
|
|
|
1684
1698
|
category: input.category,
|
|
1685
1699
|
version: input.version,
|
|
1686
1700
|
odataVersion: input.odataVersion,
|
|
1701
|
+
// Function-module create needs the parent function-group name for the
|
|
1702
|
+
// <adtcore:containerRef> in the create payload (issue #250).
|
|
1703
|
+
group: input.group,
|
|
1687
1704
|
};
|
|
1688
1705
|
return props;
|
|
1689
1706
|
}
|
|
@@ -2082,6 +2099,30 @@ export function buildCreateXml(type, name, pkg, description, properties) {
|
|
|
2082
2099
|
};
|
|
2083
2100
|
return buildMessageClassXml(params);
|
|
2084
2101
|
}
|
|
2102
|
+
case 'FUGR':
|
|
2103
|
+
// Function group create envelope. POSTed to /sap/bc/adt/functions/groups
|
|
2104
|
+
// with Content-Type: application/vnd.sap.adt.functions.groups.v3+xml.
|
|
2105
|
+
// Verified live on a4h S/4HANA 2023 (issue #250).
|
|
2106
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2107
|
+
<group:abapFunctionGroup xmlns:group="http://www.sap.com/adt/functions/groups" xmlns:adtcore="http://www.sap.com/adt/core" adtcore:description="${escapeXml(description)}" adtcore:language="EN" adtcore:name="${escapeXml(name)}" adtcore:type="FUGR/F" adtcore:masterLanguage="EN">
|
|
2108
|
+
<adtcore:packageRef adtcore:name="${escapeXml(pkg)}"/>
|
|
2109
|
+
</group:abapFunctionGroup>`;
|
|
2110
|
+
case 'FUNC': {
|
|
2111
|
+
// Function module create envelope. POSTed to
|
|
2112
|
+
// /sap/bc/adt/functions/groups/{group_lc}/fmodules with
|
|
2113
|
+
// Content-Type: application/vnd.sap.adt.functions.fmodules+xml.
|
|
2114
|
+
// No <adtcore:packageRef> — FM inherits package from the parent FUGR.
|
|
2115
|
+
// adtcore:uri must be lowercase (verified live on a4h).
|
|
2116
|
+
const group = String(properties?.group ?? '').trim();
|
|
2117
|
+
if (!group) {
|
|
2118
|
+
throw new Error('FUNC create requires "group" property — pass it via SAPWrite args (the parent function group must already exist).');
|
|
2119
|
+
}
|
|
2120
|
+
const groupLc = encodeURIComponent(group.toLowerCase());
|
|
2121
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2122
|
+
<fmodule:abapFunctionModule xmlns:fmodule="http://www.sap.com/adt/functions/fmodules" xmlns:adtcore="http://www.sap.com/adt/core" adtcore:description="${escapeXml(description)}" adtcore:name="${escapeXml(name)}" adtcore:type="FUGR/FF">
|
|
2123
|
+
<adtcore:containerRef adtcore:name="${escapeXml(group)}" adtcore:type="FUGR/F" adtcore:uri="/sap/bc/adt/functions/groups/${groupLc}"/>
|
|
2124
|
+
</fmodule:abapFunctionModule>`;
|
|
2125
|
+
}
|
|
2085
2126
|
default:
|
|
2086
2127
|
// Fallback — generic objectReferences using the correct URL for the type
|
|
2087
2128
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -2090,6 +2131,27 @@ export function buildCreateXml(type, name, pkg, description, properties) {
|
|
|
2090
2131
|
</adtcore:objectReferences>`;
|
|
2091
2132
|
}
|
|
2092
2133
|
}
|
|
2134
|
+
/**
|
|
2135
|
+
* Strip SAPGUI-style function-module parameter comment blocks from an FM source body.
|
|
2136
|
+
*
|
|
2137
|
+
* SAP rejects PUT-to-source/main with parameter comment blocks (verified live on a4h
|
|
2138
|
+
* S/4HANA 2023 — issue #250):
|
|
2139
|
+
* HTTP 400 / com.sap.adt.sedi / ExceptionResourceScanDuringSaveFailure
|
|
2140
|
+
* "Parameter comment blocks are not allowed" (T100KEY FUNC_ADT028)
|
|
2141
|
+
*
|
|
2142
|
+
* The signature is metadata, not source. LLMs frequently emit the SAPGUI block out
|
|
2143
|
+
* of muscle memory (every released FM ships with one). This helper strips lines whose
|
|
2144
|
+
* first non-whitespace tokens are `*"` so the PUT succeeds, and reports back whether
|
|
2145
|
+
* stripping occurred so the caller can append a warning to the response.
|
|
2146
|
+
*
|
|
2147
|
+
* Only `*"…` lines are stripped — single `*` ABAP comments and inline `"` comments
|
|
2148
|
+
* are preserved. Exported for unit tests.
|
|
2149
|
+
*/
|
|
2150
|
+
export function stripFmParamCommentBlock(source) {
|
|
2151
|
+
const lines = source.split('\n');
|
|
2152
|
+
const kept = lines.filter((line) => !/^\s*\*"/.test(line));
|
|
2153
|
+
return { source: kept.join('\n'), wasStripped: kept.length !== lines.length };
|
|
2154
|
+
}
|
|
2093
2155
|
/** Escape special characters for XML attribute values */
|
|
2094
2156
|
function escapeXml(s) {
|
|
2095
2157
|
return s
|
|
@@ -2453,12 +2515,42 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2453
2515
|
// (transparent) or /structures/ (DDIC structure). Resolve once via the client's
|
|
2454
2516
|
// cached URL probe. For 'create' the default /tables/ URL is correct (we only
|
|
2455
2517
|
// create transparent tables today; structure creation is out of scope).
|
|
2518
|
+
//
|
|
2519
|
+
// For FUNC, the URL has the parent function group baked into the path:
|
|
2520
|
+
// /sap/bc/adt/functions/groups/{group_lc}/fmodules/{name_lc}
|
|
2521
|
+
// `objectBasePath('FUNC')` deliberately throws (PR #223 — generic URL builders
|
|
2522
|
+
// must fail loudly for FM since they can't know the parent group). Issue #250:
|
|
2523
|
+
// we pre-resolve the URL here from `args.group` (required for create; auto-
|
|
2524
|
+
// resolved via search for update/delete) so the action switch downstream uses
|
|
2525
|
+
// the correct URL. We also mirror the resolved group back onto args so
|
|
2526
|
+
// `buildCreateXml('FUNC', …, properties)` finds it.
|
|
2456
2527
|
let objectUrl;
|
|
2457
2528
|
let srcUrl;
|
|
2458
2529
|
if (type === 'TABL' && action !== 'create' && action !== 'batch_create') {
|
|
2459
2530
|
objectUrl = await client.resolveTablObjectUrl(name);
|
|
2460
2531
|
srcUrl = `${objectUrl}/source/main`;
|
|
2461
2532
|
}
|
|
2533
|
+
else if (type === 'FUNC') {
|
|
2534
|
+
let group = String(args.group ?? '').trim();
|
|
2535
|
+
if (!group) {
|
|
2536
|
+
if (action === 'create') {
|
|
2537
|
+
return errorResult('"group" is required to create a FUNC. Create the parent function group first (SAPWrite type=FUGR) or pass group explicitly.');
|
|
2538
|
+
}
|
|
2539
|
+
// For update/delete try to auto-resolve the group via search
|
|
2540
|
+
const resolved = cachingLayer
|
|
2541
|
+
? await cachingLayer.resolveFuncGroup(client, name)
|
|
2542
|
+
: await client.resolveFunctionGroup(name);
|
|
2543
|
+
if (!resolved) {
|
|
2544
|
+
return errorResult(`Cannot resolve function group for FM "${name}". Provide the "group" parameter explicitly, or use SAPSearch to find the parent group.`);
|
|
2545
|
+
}
|
|
2546
|
+
group = resolved;
|
|
2547
|
+
}
|
|
2548
|
+
const groupLc = encodeURIComponent(group.toLowerCase());
|
|
2549
|
+
objectUrl = `/sap/bc/adt/functions/groups/${groupLc}/fmodules/${encodeURIComponent(name.toLowerCase())}`;
|
|
2550
|
+
srcUrl = `${objectUrl}/source/main`;
|
|
2551
|
+
// Pass the resolved group through to buildCreateXml via args.group
|
|
2552
|
+
args.group = group;
|
|
2553
|
+
}
|
|
2462
2554
|
else {
|
|
2463
2555
|
objectUrl = objectUrlForType(type, name);
|
|
2464
2556
|
srcUrl = sourceUrlForType(type, name);
|
|
@@ -2514,19 +2606,34 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2514
2606
|
const cdsGuardUpdate = guardCdsSyntax(type, source, cachedFeatures);
|
|
2515
2607
|
if (cdsGuardUpdate)
|
|
2516
2608
|
return cdsGuardUpdate;
|
|
2517
|
-
//
|
|
2518
|
-
|
|
2609
|
+
// FUNC-source sanitization: strip SAPGUI-style parameter comment blocks.
|
|
2610
|
+
// SAP rejects PUT-to-source/main with these blocks (HTTP 400 / FUNC_ADT028
|
|
2611
|
+
// "Parameter comment blocks are not allowed" — verified live a4h S/4HANA 2023,
|
|
2612
|
+
// issue #250). LLMs frequently emit them out of muscle memory because every
|
|
2613
|
+
// released FM has one. Strip and warn rather than fail.
|
|
2614
|
+
let effectiveSource = source;
|
|
2615
|
+
let fmParamStripWarning;
|
|
2616
|
+
if (type === 'FUNC') {
|
|
2617
|
+
const stripped = stripFmParamCommentBlock(source);
|
|
2618
|
+
effectiveSource = stripped.source;
|
|
2619
|
+
if (stripped.wasStripped) {
|
|
2620
|
+
fmParamStripWarning =
|
|
2621
|
+
'Stripped *"…IMPORTING/EXPORTING…*" parameter comment blocks (SAP rejects them on PUT — manage FM parameters via SAPGUI/Eclipse).';
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
// Pre-write lint validation (uses sanitized source for FUNC)
|
|
2625
|
+
const lintWarnings = runPreWriteLint(effectiveSource, type, name, config, lintOverride);
|
|
2519
2626
|
if (lintWarnings.blocked)
|
|
2520
2627
|
return lintWarnings.result;
|
|
2521
2628
|
// Pre-write server-side syntax check (opt-in; never blocks — warnings only).
|
|
2522
|
-
const checkNotes = await runPreWriteSyntaxCheck(client, type,
|
|
2629
|
+
const checkNotes = await runPreWriteSyntaxCheck(client, type, effectiveSource, objectUrl, config, checkOverride);
|
|
2523
2630
|
// If safeUpdateSource throws (lock conflict, network error, etc.), checkNotes
|
|
2524
2631
|
// is intentionally discarded — pre-check warnings only matter when the write succeeded.
|
|
2525
|
-
await safeUpdateSource(client.http, client.safety, objectUrl, srcUrl,
|
|
2632
|
+
await safeUpdateSource(client.http, client.safety, objectUrl, srcUrl, effectiveSource, transport, cachedFeatures?.abapRelease);
|
|
2526
2633
|
invalidateWrittenObject(type, name);
|
|
2527
2634
|
const msg = `Successfully updated ${type} ${name}.`;
|
|
2528
2635
|
const cdsUpdateHint = type === 'DDLS' ? await buildCdsUpdateCrudHint(client, name, objectUrl) : undefined;
|
|
2529
|
-
const warnings = mergePreWriteWarnings(preflightWarnings.warnings, lintWarnings.warnings, checkNotes, cdsUpdateHint);
|
|
2636
|
+
const warnings = mergePreWriteWarnings(preflightWarnings.warnings, lintWarnings.warnings, checkNotes, cdsUpdateHint, fmParamStripWarning);
|
|
2530
2637
|
return warnings ? textResult(`${msg}\n\n${warnings}`) : textResult(msg);
|
|
2531
2638
|
}
|
|
2532
2639
|
case 'create': {
|
|
@@ -2697,15 +2804,26 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2697
2804
|
}
|
|
2698
2805
|
// Step 2: Write source code if provided
|
|
2699
2806
|
if (source) {
|
|
2807
|
+
// FUNC: strip SAPGUI parameter comment blocks (see update path for rationale).
|
|
2808
|
+
let createSource = source;
|
|
2809
|
+
let fmParamStripWarning;
|
|
2810
|
+
if (type === 'FUNC') {
|
|
2811
|
+
const stripped = stripFmParamCommentBlock(source);
|
|
2812
|
+
createSource = stripped.source;
|
|
2813
|
+
if (stripped.wasStripped) {
|
|
2814
|
+
fmParamStripWarning =
|
|
2815
|
+
'Stripped *"…IMPORTING/EXPORTING…*" parameter comment blocks (manage FM parameters via SAPGUI/Eclipse).';
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2700
2818
|
// Pre-write lint validation
|
|
2701
|
-
const lintWarnings = runPreWriteLint(
|
|
2819
|
+
const lintWarnings = runPreWriteLint(createSource, type, name, config, lintOverride);
|
|
2702
2820
|
if (lintWarnings.blocked) {
|
|
2703
2821
|
return textResult(`Created ${type} ${name} in package ${pkg}, but source was rejected by lint:\n${lintWarnings.result.content[0].text}`);
|
|
2704
2822
|
}
|
|
2705
|
-
await safeUpdateSource(client.http, client.safety, objectUrl, srcUrl,
|
|
2823
|
+
await safeUpdateSource(client.http, client.safety, objectUrl, srcUrl, createSource, effectiveTransport, cachedFeatures?.abapRelease);
|
|
2706
2824
|
invalidateWrittenObject(type, name);
|
|
2707
2825
|
const msg = `Created ${type} ${name} in package ${pkg} and wrote source code.`;
|
|
2708
|
-
const warnings = mergePreWriteWarnings(preflightWarnings.warnings, lintWarnings.warnings);
|
|
2826
|
+
const warnings = mergePreWriteWarnings(preflightWarnings.warnings, lintWarnings.warnings, fmParamStripWarning);
|
|
2709
2827
|
return warnings ? textResult(`${msg}\n\n${warnings}`) : textResult(msg);
|
|
2710
2828
|
}
|
|
2711
2829
|
return textResult(`Created ${type} ${name} in package ${pkg}.\n${result}`);
|
|
@@ -3421,10 +3539,33 @@ async function handleSAPActivate(client, args, cachingLayer) {
|
|
|
3421
3539
|
// Resolve URLs sequentially. For TABL we await the URL resolver so DDIC
|
|
3422
3540
|
// structures (which live at /sap/bc/adt/ddic/structures/) are addressed
|
|
3423
3541
|
// correctly; the resolver short-circuits on its in-memory cache.
|
|
3542
|
+
// For FUNC the URL needs the parent function-group baked into the path
|
|
3543
|
+
// (issue #250); each batch entry must carry `group` or be auto-resolvable
|
|
3544
|
+
// by name.
|
|
3424
3545
|
const objects = await Promise.all(rawObjects.map(async (o) => {
|
|
3425
3546
|
const objType = normalizeObjectType(String(o.type ?? type));
|
|
3426
3547
|
const objName = String(o.name ?? '');
|
|
3427
|
-
|
|
3548
|
+
let url;
|
|
3549
|
+
if (objType === 'TABL') {
|
|
3550
|
+
url = await client.resolveTablObjectUrl(objName);
|
|
3551
|
+
}
|
|
3552
|
+
else if (objType === 'FUNC') {
|
|
3553
|
+
let group = String(o.group ?? args.group ?? '').trim();
|
|
3554
|
+
if (!group) {
|
|
3555
|
+
const resolved = cachingLayer
|
|
3556
|
+
? await cachingLayer.resolveFuncGroup(client, objName)
|
|
3557
|
+
: await client.resolveFunctionGroup(objName);
|
|
3558
|
+
if (!resolved) {
|
|
3559
|
+
throw new Error(`Cannot resolve function group for FM "${objName}" in batch activate. Provide "group" on each FUNC entry.`);
|
|
3560
|
+
}
|
|
3561
|
+
group = resolved;
|
|
3562
|
+
}
|
|
3563
|
+
const groupLc = encodeURIComponent(group.toLowerCase());
|
|
3564
|
+
url = `/sap/bc/adt/functions/groups/${groupLc}/fmodules/${encodeURIComponent(objName.toLowerCase())}`;
|
|
3565
|
+
}
|
|
3566
|
+
else {
|
|
3567
|
+
url = objectUrlForType(objType, objName);
|
|
3568
|
+
}
|
|
3428
3569
|
return { type: objType, name: objName, url };
|
|
3429
3570
|
}));
|
|
3430
3571
|
const result = await activateBatch(client.http, client.safety, objects, activateOpts);
|
|
@@ -3451,7 +3592,30 @@ async function handleSAPActivate(client, args, cachingLayer) {
|
|
|
3451
3592
|
// Single activation (existing behavior). For TABL we resolve the URL because
|
|
3452
3593
|
// the existing object may live at /tables/ (transparent) or /structures/
|
|
3453
3594
|
// (DDIC structure); using the wrong one would produce a confusing 404.
|
|
3454
|
-
|
|
3595
|
+
// For FUNC the URL needs the parent function group baked into the path
|
|
3596
|
+
// (issue #250) — `objectBasePath('FUNC')` deliberately throws so generic
|
|
3597
|
+
// builders fail loudly. Auto-resolve the group when omitted.
|
|
3598
|
+
let objectUrl;
|
|
3599
|
+
if (type === 'TABL') {
|
|
3600
|
+
objectUrl = await client.resolveTablObjectUrl(name);
|
|
3601
|
+
}
|
|
3602
|
+
else if (type === 'FUNC') {
|
|
3603
|
+
let group = String(args.group ?? '').trim();
|
|
3604
|
+
if (!group) {
|
|
3605
|
+
const resolved = cachingLayer
|
|
3606
|
+
? await cachingLayer.resolveFuncGroup(client, name)
|
|
3607
|
+
: await client.resolveFunctionGroup(name);
|
|
3608
|
+
if (!resolved) {
|
|
3609
|
+
return errorResult(`Cannot resolve function group for FM "${name}". Provide the "group" parameter explicitly.`);
|
|
3610
|
+
}
|
|
3611
|
+
group = resolved;
|
|
3612
|
+
}
|
|
3613
|
+
const groupLc = encodeURIComponent(group.toLowerCase());
|
|
3614
|
+
objectUrl = `/sap/bc/adt/functions/groups/${groupLc}/fmodules/${encodeURIComponent(name.toLowerCase())}`;
|
|
3615
|
+
}
|
|
3616
|
+
else {
|
|
3617
|
+
objectUrl = objectUrlForType(type, name);
|
|
3618
|
+
}
|
|
3455
3619
|
const result = await activate(client.http, client.safety, objectUrl, { ...activateOpts, name });
|
|
3456
3620
|
if (result.success) {
|
|
3457
3621
|
cachingLayer?.invalidate(type, name, 'all');
|