@linkup-ai/abap-ai 2.0.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/README.md +384 -0
- package/dist/adt-client.js +364 -0
- package/dist/cli/activate.js +113 -0
- package/dist/cli/init.js +333 -0
- package/dist/cli/remove.js +80 -0
- package/dist/cli/status.js +229 -0
- package/dist/cli/systems.js +68 -0
- package/dist/cli.js +81 -0
- package/dist/index.js +1318 -0
- package/dist/knowledge/abap/abap-dictionary.md +199 -0
- package/dist/knowledge/abap/abap-sql.md +296 -0
- package/dist/knowledge/abap/amdp.md +273 -0
- package/dist/knowledge/abap/clean-code.md +293 -0
- package/dist/knowledge/abap/cloud-background-processing.md +250 -0
- package/dist/knowledge/abap/cloud-communication.md +265 -0
- package/dist/knowledge/abap/cloud-development.md +176 -0
- package/dist/knowledge/abap/cloud-extensibility.md +252 -0
- package/dist/knowledge/abap/cloud-released-apis.md +261 -0
- package/dist/knowledge/abap/constructor-expressions.md +289 -0
- package/dist/knowledge/abap/enhancements.md +232 -0
- package/dist/knowledge/abap/exceptions.md +271 -0
- package/dist/knowledge/abap/internal-tables.md +205 -0
- package/dist/knowledge/abap/object-orientation.md +298 -0
- package/dist/knowledge/abap/performance.md +216 -0
- package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
- package/dist/knowledge/abap/rap-business-events.md +216 -0
- package/dist/knowledge/abap/rap-draft.md +191 -0
- package/dist/knowledge/abap/rap-eml.md +453 -0
- package/dist/knowledge/abap/rap-end-to-end.md +486 -0
- package/dist/knowledge/abap/rap-feature-control.md +185 -0
- package/dist/knowledge/abap/rap-numbering.md +280 -0
- package/dist/knowledge/abap/rap-service-exposure.md +163 -0
- package/dist/knowledge/abap/rap-unmanaged.md +468 -0
- package/dist/knowledge/abap/string-processing.md +180 -0
- package/dist/knowledge/abap/unit-testing.md +303 -0
- package/dist/knowledge/abap-cds/access-control.md +241 -0
- package/dist/knowledge/abap-cds/annotations.md +331 -0
- package/dist/knowledge/abap-cds/associations.md +254 -0
- package/dist/knowledge/abap-cds/expressions.md +230 -0
- package/dist/knowledge/abap-cds/functions.md +245 -0
- package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
- package/dist/knowledge/cap/authentication.md +278 -0
- package/dist/knowledge/cap/cdl-syntax.md +247 -0
- package/dist/knowledge/cap/cql-queries.md +266 -0
- package/dist/knowledge/cap/deployment.md +343 -0
- package/dist/knowledge/cap/event-handlers.md +287 -0
- package/dist/knowledge/cap/fiori-integration.md +303 -0
- package/dist/knowledge/cap/service-definitions.md +287 -0
- package/dist/knowledge/fiori/annotations.md +347 -0
- package/dist/knowledge/fiori/deployment.md +340 -0
- package/dist/knowledge/fiori/fiori-elements.md +332 -0
- package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
- package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
- package/dist/knowledge/fiori/ui5-controllers.md +358 -0
- package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
- package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
- package/dist/knowledge/fiori/ui5-manifest.md +411 -0
- package/dist/knowledge/fiori/ui5-routing.md +303 -0
- package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
- package/dist/logger.js +114 -0
- package/dist/system-profile.js +207 -0
- package/dist/tools/abap-doc.js +72 -0
- package/dist/tools/abapgit.js +161 -0
- package/dist/tools/activate.js +68 -0
- package/dist/tools/atc-check.js +117 -0
- package/dist/tools/auth-object.js +56 -0
- package/dist/tools/breakpoints.js +76 -0
- package/dist/tools/call-hierarchy.js +84 -0
- package/dist/tools/cds-annotations.js +98 -0
- package/dist/tools/cds-dependencies.js +65 -0
- package/dist/tools/check.js +47 -0
- package/dist/tools/code-completion.js +70 -0
- package/dist/tools/code-coverage.js +111 -0
- package/dist/tools/create-amdp.js +111 -0
- package/dist/tools/create-dcl.js +81 -0
- package/dist/tools/create-transport.js +38 -0
- package/dist/tools/create.js +285 -0
- package/dist/tools/data-preview.js +37 -0
- package/dist/tools/delete.js +45 -0
- package/dist/tools/deploy-bsp.js +298 -0
- package/dist/tools/discovery.js +59 -0
- package/dist/tools/element-info.js +93 -0
- package/dist/tools/enhancements.js +186 -0
- package/dist/tools/extract-method.js +44 -0
- package/dist/tools/function-group.js +59 -0
- package/dist/tools/knowledge.js +275 -0
- package/dist/tools/lock-object.js +75 -0
- package/dist/tools/message-class.js +67 -0
- package/dist/tools/navigate.js +80 -0
- package/dist/tools/number-range.js +57 -0
- package/dist/tools/object-documentation.js +43 -0
- package/dist/tools/object-structure.js +78 -0
- package/dist/tools/object-versions.js +57 -0
- package/dist/tools/package-contents.js +60 -0
- package/dist/tools/pretty-printer.js +35 -0
- package/dist/tools/publish-binding.js +49 -0
- package/dist/tools/quick-fix.js +69 -0
- package/dist/tools/read.js +167 -0
- package/dist/tools/refactor-rename.js +60 -0
- package/dist/tools/release-transport.js +24 -0
- package/dist/tools/released-apis.js +51 -0
- package/dist/tools/repository-tree.js +90 -0
- package/dist/tools/scaffold-rap.js +642 -0
- package/dist/tools/search.js +73 -0
- package/dist/tools/shared/data-format.js +101 -0
- package/dist/tools/sql-console.js +17 -0
- package/dist/tools/system-info.js +270 -0
- package/dist/tools/traces.js +66 -0
- package/dist/tools/transport-contents.js +83 -0
- package/dist/tools/transports.js +67 -0
- package/dist/tools/unit-test.js +135 -0
- package/dist/tools/where-used.js +59 -0
- package/dist/tools/write.js +101 -0
- package/package.json +49 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapDiscovery = abapDiscovery;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapDiscovery() {
|
|
6
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
7
|
+
const response = await adt_client_js_1.http.get("/discovery", {
|
|
8
|
+
headers: { Accept: "application/atomsvc+xml" },
|
|
9
|
+
responseType: "text",
|
|
10
|
+
});
|
|
11
|
+
const xml = response.data;
|
|
12
|
+
// Parse collections
|
|
13
|
+
const collections = [];
|
|
14
|
+
const collRegex = /<app:collection[^>]*href\s*=\s*"([^"]*)"[^>]*>([\s\S]*?)<\/app:collection>/gi;
|
|
15
|
+
let match;
|
|
16
|
+
while ((match = collRegex.exec(xml)) !== null) {
|
|
17
|
+
const href = match[1];
|
|
18
|
+
const content = match[2];
|
|
19
|
+
const titleMatch = content.match(/<atom:title[^>]*>([\s\S]*?)<\/atom:title>/i)
|
|
20
|
+
?? content.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
21
|
+
const title = titleMatch?.[1]?.trim() ?? "";
|
|
22
|
+
const contentTypes = [];
|
|
23
|
+
const ctRegex = /<app:accept[^>]*>([\s\S]*?)<\/app:accept>/gi;
|
|
24
|
+
let ctMatch;
|
|
25
|
+
while ((ctMatch = ctRegex.exec(content)) !== null) {
|
|
26
|
+
contentTypes.push(ctMatch[1].trim());
|
|
27
|
+
}
|
|
28
|
+
collections.push({ href, title, contentTypes });
|
|
29
|
+
}
|
|
30
|
+
// Fallback: simpler parsing
|
|
31
|
+
if (collections.length === 0) {
|
|
32
|
+
const simpleRegex = /<collection[^>]*href\s*=\s*"([^"]*)"[^>]*>/gi;
|
|
33
|
+
while ((match = simpleRegex.exec(xml)) !== null) {
|
|
34
|
+
const href = match[0];
|
|
35
|
+
const hrefVal = match[1];
|
|
36
|
+
const titleMatch = href.match(/title\s*=\s*"([^"]*)"/i);
|
|
37
|
+
collections.push({
|
|
38
|
+
href: hrefVal,
|
|
39
|
+
title: titleMatch?.[1] ?? "",
|
|
40
|
+
contentTypes: [],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (collections.length === 0) {
|
|
45
|
+
return "Discovery: nenhuma coleção ADT encontrada. Verifique a conectividade.";
|
|
46
|
+
}
|
|
47
|
+
const lines = [];
|
|
48
|
+
lines.push(`ADT Discovery: ${collections.length} APIs/serviços disponíveis`);
|
|
49
|
+
lines.push("═".repeat(70));
|
|
50
|
+
for (const c of collections) {
|
|
51
|
+
const title = c.title ? `${c.title}` : "(sem título)";
|
|
52
|
+
lines.push(` ${title}`);
|
|
53
|
+
lines.push(` URI: ${c.href}`);
|
|
54
|
+
if (c.contentTypes.length > 0) {
|
|
55
|
+
lines.push(` Tipos: ${c.contentTypes.join(", ")}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapElementInfo = abapElementInfo;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
function resolveSourceUri(objectType, objectName, classInclude) {
|
|
6
|
+
const adtPath = (0, adt_client_js_1.resolveAdtPath)(objectType);
|
|
7
|
+
if (objectType === "CLAS/OC" && classInclude && classInclude !== "main") {
|
|
8
|
+
return `/${adtPath}/${objectName}/includes/${classInclude}/source/main`;
|
|
9
|
+
}
|
|
10
|
+
return `/${adtPath}/${objectName}/source/main`;
|
|
11
|
+
}
|
|
12
|
+
function parseElementInfo(xml) {
|
|
13
|
+
const detail = {
|
|
14
|
+
name: "",
|
|
15
|
+
type: "",
|
|
16
|
+
description: "",
|
|
17
|
+
uri: "",
|
|
18
|
+
parameters: [],
|
|
19
|
+
};
|
|
20
|
+
// Nome do elemento
|
|
21
|
+
const nameMatch = xml.match(/adtcore:name\s*=\s*"([^"]*)"/i);
|
|
22
|
+
if (nameMatch)
|
|
23
|
+
detail.name = nameMatch[1];
|
|
24
|
+
// Tipo
|
|
25
|
+
const typeMatch = xml.match(/adtcore:type\s*=\s*"([^"]*)"/i);
|
|
26
|
+
if (typeMatch)
|
|
27
|
+
detail.type = typeMatch[1];
|
|
28
|
+
// Descrição
|
|
29
|
+
const descMatch = xml.match(/adtcore:description\s*=\s*"([^"]*)"/i);
|
|
30
|
+
if (descMatch)
|
|
31
|
+
detail.description = descMatch[1];
|
|
32
|
+
// URI
|
|
33
|
+
const uriMatch = xml.match(/adtcore:uri\s*=\s*"([^"]*)"/i);
|
|
34
|
+
if (uriMatch)
|
|
35
|
+
detail.uri = uriMatch[1];
|
|
36
|
+
// Parâmetros (para métodos/function modules)
|
|
37
|
+
const paramRegex = /<[^>]*?(?:parameter|param)[^>]*?adtcore:name\s*=\s*"([^"]*)"[^>]*>/gi;
|
|
38
|
+
let match;
|
|
39
|
+
while ((match = paramRegex.exec(xml)) !== null) {
|
|
40
|
+
const element = match[0];
|
|
41
|
+
const name = match[1];
|
|
42
|
+
const dirMatch = element.match(/direction\s*=\s*"([^"]*)"/i);
|
|
43
|
+
const typeMatch = element.match(/(?:dataType|abapType|type)\s*=\s*"([^"]*)"/i);
|
|
44
|
+
detail.parameters.push({
|
|
45
|
+
name,
|
|
46
|
+
direction: dirMatch?.[1] ?? "",
|
|
47
|
+
type: typeMatch?.[1] ?? "",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return detail;
|
|
51
|
+
}
|
|
52
|
+
function formatElementInfo(detail) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
if (detail.name)
|
|
55
|
+
lines.push(`Nome: ${detail.name}`);
|
|
56
|
+
if (detail.type)
|
|
57
|
+
lines.push(`Tipo: ${detail.type}`);
|
|
58
|
+
if (detail.description)
|
|
59
|
+
lines.push(`Descrição: ${detail.description}`);
|
|
60
|
+
if (detail.uri)
|
|
61
|
+
lines.push(`URI: ${detail.uri}`);
|
|
62
|
+
if (detail.parameters.length > 0) {
|
|
63
|
+
lines.push("");
|
|
64
|
+
lines.push(`Parâmetros (${detail.parameters.length}):`);
|
|
65
|
+
lines.push("─".repeat(50));
|
|
66
|
+
for (const p of detail.parameters) {
|
|
67
|
+
const dir = p.direction ? `[${p.direction}]` : "";
|
|
68
|
+
const type = p.type ? `: ${p.type}` : "";
|
|
69
|
+
lines.push(` ${p.name.padEnd(30)} ${dir.padEnd(12)} ${type}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (lines.length === 0) {
|
|
73
|
+
return "Nenhuma informação encontrada para o elemento na posição indicada.";
|
|
74
|
+
}
|
|
75
|
+
return lines.join("\n");
|
|
76
|
+
}
|
|
77
|
+
async function abapElementInfo(input) {
|
|
78
|
+
const { object_type, object_name, line, column, class_include } = input;
|
|
79
|
+
const name = object_name.toUpperCase();
|
|
80
|
+
const uri = resolveSourceUri(object_type, name, class_include);
|
|
81
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
82
|
+
const response = await adt_client_js_1.http.get("/repository/informationsystem/elementinfo", {
|
|
83
|
+
params: { uri: `/sap/bc/adt${uri}`, line: String(line), column: String(column) },
|
|
84
|
+
headers: { Accept: "application/xml" },
|
|
85
|
+
responseType: "text",
|
|
86
|
+
validateStatus: (status) => status < 500,
|
|
87
|
+
});
|
|
88
|
+
if (response.status === 404) {
|
|
89
|
+
throw new Error("Endpoint elementinfo não disponível neste sistema SAP. Verifique o serviço /sap/bc/adt/repository/informationsystem/elementinfo.");
|
|
90
|
+
}
|
|
91
|
+
const detail = parseElementInfo(response.data);
|
|
92
|
+
return formatElementInfo(detail);
|
|
93
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapEnhancementSpot = abapEnhancementSpot;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
function parseBoolAttr(element, ...attrNames) {
|
|
6
|
+
for (const attr of attrNames) {
|
|
7
|
+
const match = element.match(new RegExp(`${attr}\\s*=\\s*"([^"]*)"`, "i"));
|
|
8
|
+
if (match) {
|
|
9
|
+
const val = match[1].toLowerCase();
|
|
10
|
+
return val === "true" || val === "x" || val === "1";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
async function abapEnhancementSpot(input) {
|
|
16
|
+
const { name, type = "spot" } = input;
|
|
17
|
+
const objName = name.toUpperCase();
|
|
18
|
+
const path = type === "implementation"
|
|
19
|
+
? `/enhancements/implementations/${objName}`
|
|
20
|
+
: `/enhancements/spots/${objName}`;
|
|
21
|
+
const xml = await (0, adt_client_js_1.adtGetXml)(path);
|
|
22
|
+
// Parse BAdI definitions / Enhancement options
|
|
23
|
+
const badis = [];
|
|
24
|
+
const badiRegex = /<(?:enh:)?(?:badi|enhancementOption|badiDefinition)[^>]*\/?>/gi;
|
|
25
|
+
let match;
|
|
26
|
+
while ((match = badiRegex.exec(xml)) !== null) {
|
|
27
|
+
const el = match[0];
|
|
28
|
+
const nameMatch = el.match(/(?:adtcore:)?name\s*=\s*"([^"]*)"/i);
|
|
29
|
+
const descMatch = el.match(/(?:adtcore:)?description\s*=\s*"([^"]*)"/i);
|
|
30
|
+
const intfMatch = el.match(/(?:interface|fallbackClass|badiInterface)\s*=\s*"([^"]*)"/i);
|
|
31
|
+
// Detectar single-use vs multi-use
|
|
32
|
+
const multipleUse = parseBoolAttr(el, "multipleUse", "multiple_use", "isMultipleUse");
|
|
33
|
+
const filterDependent = parseBoolAttr(el, "filterDependent", "filter_dependent", "isFilterDependent", "usesFilter");
|
|
34
|
+
if (nameMatch) {
|
|
35
|
+
badis.push({
|
|
36
|
+
name: nameMatch[1],
|
|
37
|
+
description: descMatch?.[1] ?? "",
|
|
38
|
+
interface: intfMatch?.[1] ?? "",
|
|
39
|
+
multipleUse,
|
|
40
|
+
filterDependent,
|
|
41
|
+
implementations: [],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Parse implementing classes
|
|
46
|
+
const impls = [];
|
|
47
|
+
const implRegex = /<(?:enh:)?(?:implementation|badiImplementation|implClass)[^>]*\/?>/gi;
|
|
48
|
+
while ((match = implRegex.exec(xml)) !== null) {
|
|
49
|
+
const el = match[0];
|
|
50
|
+
const nameMatch = el.match(/(?:adtcore:)?name\s*=\s*"([^"]*)"/i);
|
|
51
|
+
const descMatch = el.match(/(?:adtcore:)?description\s*=\s*"([^"]*)"/i);
|
|
52
|
+
const badiMatch = el.match(/(?:badi|badiName|badiDefinition)\s*=\s*"([^"]*)"/i);
|
|
53
|
+
const activeMatch = parseBoolAttr(el, "active", "isActive");
|
|
54
|
+
if (nameMatch) {
|
|
55
|
+
impls.push({
|
|
56
|
+
name: nameMatch[1],
|
|
57
|
+
description: descMatch?.[1] ?? "",
|
|
58
|
+
badiName: badiMatch?.[1] ?? "",
|
|
59
|
+
active: activeMatch !== false, // default true if not specified
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Associar implementações aos BAdIs
|
|
64
|
+
for (const impl of impls) {
|
|
65
|
+
const badi = badis.find((b) => b.name === impl.badiName);
|
|
66
|
+
if (badi) {
|
|
67
|
+
badi.implementations.push({ name: impl.name, description: impl.description, active: impl.active });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Se é um spot, tentar buscar implementações existentes via search
|
|
71
|
+
if (type === "spot" && badis.length > 0) {
|
|
72
|
+
await tryLoadExistingImplementations(badis);
|
|
73
|
+
}
|
|
74
|
+
// Format output
|
|
75
|
+
const lines = [];
|
|
76
|
+
const typeLabel = type === "implementation" ? "Enhancement Implementation" : "Enhancement Spot";
|
|
77
|
+
lines.push(`${typeLabel}: ${objName}`);
|
|
78
|
+
lines.push("═".repeat(60));
|
|
79
|
+
const rootDesc = xml.match(/adtcore:description\s*=\s*"([^"]*)"/i);
|
|
80
|
+
if (rootDesc)
|
|
81
|
+
lines.push(`Descrição: ${rootDesc[1]}`);
|
|
82
|
+
if (badis.length > 0) {
|
|
83
|
+
lines.push(`\nBAdIs / Enhancement Options (${badis.length}):`);
|
|
84
|
+
lines.push("─".repeat(55));
|
|
85
|
+
for (const b of badis) {
|
|
86
|
+
const desc = b.description ? ` — ${b.description}` : "";
|
|
87
|
+
const intf = b.interface ? `\n Interface: ${b.interface}` : "";
|
|
88
|
+
// Uso: single vs multiple
|
|
89
|
+
let usageInfo = "";
|
|
90
|
+
if (b.multipleUse === true) {
|
|
91
|
+
usageInfo = "\n Uso: MÚLTIPLAS implementações permitidas";
|
|
92
|
+
}
|
|
93
|
+
else if (b.multipleUse === false) {
|
|
94
|
+
usageInfo = "\n Uso: SINGLE-USE (apenas UMA implementação permitida)";
|
|
95
|
+
}
|
|
96
|
+
if (b.filterDependent === true) {
|
|
97
|
+
usageInfo += "\n Filtro: sim (filter-dependent)";
|
|
98
|
+
}
|
|
99
|
+
// Implementações existentes
|
|
100
|
+
let implInfo = "";
|
|
101
|
+
if (b.implementations.length > 0) {
|
|
102
|
+
implInfo = `\n Implementações existentes (${b.implementations.length}):`;
|
|
103
|
+
for (const impl of b.implementations) {
|
|
104
|
+
const status = impl.active ? "ativa" : "inativa";
|
|
105
|
+
const idesc = impl.description ? ` — ${impl.description}` : "";
|
|
106
|
+
implInfo += `\n • ${impl.name} (${status})${idesc}`;
|
|
107
|
+
}
|
|
108
|
+
// ALERTA para single-use com implementação existente
|
|
109
|
+
if (b.multipleUse === false && b.implementations.length > 0) {
|
|
110
|
+
const activeImpl = b.implementations.find((i) => i.active);
|
|
111
|
+
if (activeImpl) {
|
|
112
|
+
implInfo += `\n\n ⛔ BLOQUEIO: BAdI "${b.name}" é SINGLE-USE e já possui implementação ativa "${activeImpl.name}".`;
|
|
113
|
+
implInfo += `\n NÃO é possível criar uma nova implementação. Modifique a existente.`;
|
|
114
|
+
implInfo += `\n → abap_read(object_type:'CLAS/OC', object_name:'${activeImpl.name}') para ver o código atual.`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
implInfo = "\n Implementações existentes: nenhuma encontrada";
|
|
120
|
+
if (b.multipleUse === false) {
|
|
121
|
+
implInfo += " (single-use — pode criar UMA implementação)";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
lines.push(` ${b.name}${desc}${intf}${usageInfo}${implInfo}`);
|
|
125
|
+
lines.push("");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Implementações não associadas a BAdIs
|
|
129
|
+
const unlinked = impls.filter((i) => !badis.some((b) => b.name === i.badiName));
|
|
130
|
+
if (unlinked.length > 0) {
|
|
131
|
+
lines.push(`\nImplementações (${unlinked.length}):`);
|
|
132
|
+
lines.push("─".repeat(50));
|
|
133
|
+
for (const i of unlinked) {
|
|
134
|
+
const desc = i.description ? ` — ${i.description}` : "";
|
|
135
|
+
const badi = i.badiName ? ` (BAdI: ${i.badiName})` : "";
|
|
136
|
+
const status = i.active ? "" : " [INATIVA]";
|
|
137
|
+
lines.push(` ${i.name}${status}${desc}${badi}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (badis.length === 0 && impls.length === 0 && unlinked.length === 0) {
|
|
141
|
+
lines.push(`\nConteúdo do XML (primeiros 1000 chars):\n${xml.slice(0, 1000)}`);
|
|
142
|
+
}
|
|
143
|
+
return lines.join("\n");
|
|
144
|
+
}
|
|
145
|
+
/** Best-effort: tenta buscar implementações existentes de cada BAdI */
|
|
146
|
+
async function tryLoadExistingImplementations(badis) {
|
|
147
|
+
try {
|
|
148
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
149
|
+
for (const badi of badis) {
|
|
150
|
+
if (badi.implementations.length > 0)
|
|
151
|
+
continue; // já tem do XML
|
|
152
|
+
try {
|
|
153
|
+
// Tentar ler o BAdI definition que pode listar implementações
|
|
154
|
+
const response = await adt_client_js_1.http.get(`/enhancements/spots/${badi.name.toLowerCase()}`, {
|
|
155
|
+
headers: { Accept: "application/xml" },
|
|
156
|
+
responseType: "text",
|
|
157
|
+
validateStatus: (s) => s < 500,
|
|
158
|
+
});
|
|
159
|
+
if (response.status < 400) {
|
|
160
|
+
const badiXml = response.data;
|
|
161
|
+
const implRegex = /<(?:enh:)?(?:implementation|badiImplementation|implClass)[^>]*\/?>/gi;
|
|
162
|
+
let m;
|
|
163
|
+
while ((m = implRegex.exec(badiXml)) !== null) {
|
|
164
|
+
const el = m[0];
|
|
165
|
+
const nameMatch = el.match(/(?:adtcore:)?name\s*=\s*"([^"]*)"/i);
|
|
166
|
+
const descMatch = el.match(/(?:adtcore:)?description\s*=\s*"([^"]*)"/i);
|
|
167
|
+
const activeMatch = parseBoolAttr(el, "active", "isActive");
|
|
168
|
+
if (nameMatch) {
|
|
169
|
+
badi.implementations.push({
|
|
170
|
+
name: nameMatch[1],
|
|
171
|
+
description: descMatch?.[1] ?? "",
|
|
172
|
+
active: activeMatch !== false,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Silenciar — não bloquear por falha na busca de implementações
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Silenciar
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapExtractMethod = abapExtractMethod;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
function resolveSourceUri(objectType, objectName, classInclude) {
|
|
6
|
+
const adtPath = (0, adt_client_js_1.resolveAdtPath)(objectType);
|
|
7
|
+
const name = objectName.toUpperCase();
|
|
8
|
+
if (objectType === "CLAS/OC" && classInclude && classInclude !== "main") {
|
|
9
|
+
return `/sap/bc/adt/${adtPath}/${name}/includes/${classInclude}/source/main`;
|
|
10
|
+
}
|
|
11
|
+
return `/sap/bc/adt/${adtPath}/${name}/source/main`;
|
|
12
|
+
}
|
|
13
|
+
async function abapExtractMethod(input) {
|
|
14
|
+
const { object_type, object_name, start_line, end_line, new_method_name, class_include, transport_request } = input;
|
|
15
|
+
const uri = resolveSourceUri(object_type, object_name, class_include);
|
|
16
|
+
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
17
|
+
<extractMethod:extractMethodRefactoring
|
|
18
|
+
xmlns:extractMethod="http://www.sap.com/adt/ris/extractmethod"
|
|
19
|
+
xmlns:adtcore="http://www.sap.com/adt/core">
|
|
20
|
+
<extractMethod:newMethodName>${new_method_name.toUpperCase()}</extractMethod:newMethodName>
|
|
21
|
+
<extractMethod:selection startLine="${start_line}" endLine="${end_line}"/>
|
|
22
|
+
<adtcore:objectReference adtcore:uri="${uri}"/>
|
|
23
|
+
</extractMethod:extractMethodRefactoring>`;
|
|
24
|
+
const params = {};
|
|
25
|
+
if (transport_request)
|
|
26
|
+
params.corrNr = transport_request;
|
|
27
|
+
const { data, status } = await (0, adt_client_js_1.adtPostXml)("/refactorings/extractmethod", body, params);
|
|
28
|
+
if (status >= 400) {
|
|
29
|
+
// Extrair mensagem de erro do XML
|
|
30
|
+
const msgMatch = data.match(/<(?:message|shortText)[^>]*>([\s\S]*?)<\/(?:message|shortText)>/i);
|
|
31
|
+
const errorMsg = msgMatch?.[1] ?? data.slice(0, 500);
|
|
32
|
+
throw new Error(`Extract Method falhou (HTTP ${status}): ${errorMsg}`);
|
|
33
|
+
}
|
|
34
|
+
// Contar mudanças aplicadas
|
|
35
|
+
const changes = [];
|
|
36
|
+
const changeRegex = /adtcore:uri\s*=\s*"([^"]*)"/gi;
|
|
37
|
+
let match;
|
|
38
|
+
while ((match = changeRegex.exec(data)) !== null) {
|
|
39
|
+
changes.push(match[1]);
|
|
40
|
+
}
|
|
41
|
+
return `Extract Method concluído com sucesso!\n` +
|
|
42
|
+
`Método "${new_method_name.toUpperCase()}" criado em ${object_name} (linhas ${start_line}-${end_line}).\n` +
|
|
43
|
+
`${changes.length} arquivo(s) afetado(s).`;
|
|
44
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapFunctionGroup = abapFunctionGroup;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
function parseFunctionModules(xml) {
|
|
6
|
+
const modules = [];
|
|
7
|
+
// Formato ADT: <abapsource:objectStructureElement adtcore:name="FM_NAME" adtcore:type="FUGR/FF">
|
|
8
|
+
const regex = /<abapsource:objectStructureElement[^>]*adtcore:name="([^"]*)"[^>]*adtcore:type="([^"]*)"[^>]*>/gi;
|
|
9
|
+
let match;
|
|
10
|
+
while ((match = regex.exec(xml)) !== null) {
|
|
11
|
+
const name = match[1];
|
|
12
|
+
const type = match[2];
|
|
13
|
+
// Ignorar o grupo em si (type FUGR/F)
|
|
14
|
+
if (type === "FUGR/F")
|
|
15
|
+
continue;
|
|
16
|
+
modules.push({ name, type });
|
|
17
|
+
}
|
|
18
|
+
return modules;
|
|
19
|
+
}
|
|
20
|
+
function formatFunctionModules(groupName, modules) {
|
|
21
|
+
if (modules.length === 0) {
|
|
22
|
+
return `Grupo de funções ${groupName}: nenhum function module encontrado.`;
|
|
23
|
+
}
|
|
24
|
+
const fms = modules.filter((m) => m.type === "FUGR/FF");
|
|
25
|
+
const includes = modules.filter((m) => m.type !== "FUGR/FF");
|
|
26
|
+
const lines = [
|
|
27
|
+
`Grupo de funções ${groupName} (${fms.length} function modules, ${includes.length} includes)`,
|
|
28
|
+
"─".repeat(60),
|
|
29
|
+
];
|
|
30
|
+
if (fms.length > 0) {
|
|
31
|
+
lines.push("Function Modules:");
|
|
32
|
+
for (const fm of fms) {
|
|
33
|
+
lines.push(` ${fm.name}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (includes.length > 0) {
|
|
37
|
+
lines.push("");
|
|
38
|
+
lines.push("Includes:");
|
|
39
|
+
for (const inc of includes) {
|
|
40
|
+
lines.push(` ${inc.name.padEnd(40)} ${inc.type}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
}
|
|
45
|
+
async function abapFunctionGroup(input) {
|
|
46
|
+
const { group_name } = input;
|
|
47
|
+
const name = group_name.toUpperCase();
|
|
48
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
49
|
+
const response = await adt_client_js_1.http.get(`/functions/groups/${name.toLowerCase()}/objectstructure`, {
|
|
50
|
+
headers: { Accept: "application/vnd.sap.adt.objectstructure.v2+xml" },
|
|
51
|
+
responseType: "text",
|
|
52
|
+
validateStatus: (status) => status < 500,
|
|
53
|
+
});
|
|
54
|
+
if (response.status === 404) {
|
|
55
|
+
throw new Error(`Grupo de funções ${name} não encontrado.`);
|
|
56
|
+
}
|
|
57
|
+
const modules = parseFunctionModules(response.data);
|
|
58
|
+
return formatFunctionModules(name, modules);
|
|
59
|
+
}
|