@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,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapObjectVersions = abapObjectVersions;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
function parseVersions(xml) {
|
|
6
|
+
const versions = [];
|
|
7
|
+
// Formato ADT: <version ... version="X" date="Y" author="Z" description="W" />
|
|
8
|
+
const versionRegex = /<(?:[a-zA-Z0-9_:]*)?version[^>]*>/gi;
|
|
9
|
+
let match;
|
|
10
|
+
while ((match = versionRegex.exec(xml)) !== null) {
|
|
11
|
+
const element = match[0];
|
|
12
|
+
const verMatch = element.match(/(?:version(?:Number)?)\s*=\s*"([^"]*)"/i);
|
|
13
|
+
const dateMatch = element.match(/(?:date|modifiedAt|changedAt)\s*=\s*"([^"]*)"/i);
|
|
14
|
+
const authorMatch = element.match(/(?:author|modifiedBy|changedBy)\s*=\s*"([^"]*)"/i);
|
|
15
|
+
const descMatch = element.match(/(?:description|title)\s*=\s*"([^"]*)"/i);
|
|
16
|
+
if (verMatch || dateMatch) {
|
|
17
|
+
versions.push({
|
|
18
|
+
version: verMatch?.[1] ?? "",
|
|
19
|
+
date: dateMatch?.[1] ?? "",
|
|
20
|
+
author: authorMatch?.[1] ?? "",
|
|
21
|
+
description: descMatch?.[1] ?? "",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return versions;
|
|
26
|
+
}
|
|
27
|
+
function formatVersions(objectName, versions) {
|
|
28
|
+
if (versions.length === 0) {
|
|
29
|
+
return `${objectName}: nenhuma versão encontrada.`;
|
|
30
|
+
}
|
|
31
|
+
const lines = [
|
|
32
|
+
`Versões de ${objectName} (${versions.length})`,
|
|
33
|
+
"─".repeat(70),
|
|
34
|
+
`${"Versão".padEnd(10)} ${"Data".padEnd(22)} ${"Autor".padEnd(15)} Descrição`,
|
|
35
|
+
"─".repeat(70),
|
|
36
|
+
];
|
|
37
|
+
for (const v of versions) {
|
|
38
|
+
lines.push(`${v.version.padEnd(10)} ${v.date.padEnd(22)} ${v.author.padEnd(15)} ${v.description}`);
|
|
39
|
+
}
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
}
|
|
42
|
+
async function abapObjectVersions(input) {
|
|
43
|
+
const { object_type, object_name } = input;
|
|
44
|
+
const name = object_name.toUpperCase();
|
|
45
|
+
const adtPath = (0, adt_client_js_1.resolveAdtPath)(object_type);
|
|
46
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
47
|
+
const response = await adt_client_js_1.http.get(`/${adtPath}/${name.toLowerCase()}/source/main/versions`, {
|
|
48
|
+
headers: { Accept: "application/atom+xml;type=feed" },
|
|
49
|
+
responseType: "text",
|
|
50
|
+
validateStatus: (status) => status < 500,
|
|
51
|
+
});
|
|
52
|
+
if (response.status >= 400) {
|
|
53
|
+
throw new Error(`Versões não disponíveis para ${name} (${object_type}).`);
|
|
54
|
+
}
|
|
55
|
+
const versions = parseVersions(response.data);
|
|
56
|
+
return formatVersions(name, versions);
|
|
57
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapPackageContents = abapPackageContents;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
function parsePackageContents(xml) {
|
|
6
|
+
const objects = [];
|
|
7
|
+
// Formato SAP ABAP XML: <SEU_ADT_REPOSITORY_OBJ_NODE>
|
|
8
|
+
// <OBJECT_TYPE>PROG/P</OBJECT_TYPE>
|
|
9
|
+
// <OBJECT_NAME>ZAIT_REPORT_TEST</OBJECT_NAME>
|
|
10
|
+
// <OBJECT_URI>/sap/bc/adt/programs/programs/zait_report_test</OBJECT_URI>
|
|
11
|
+
const nodeRegex = /<SEU_ADT_REPOSITORY_OBJ_NODE>([\s\S]*?)<\/SEU_ADT_REPOSITORY_OBJ_NODE>/gi;
|
|
12
|
+
let match;
|
|
13
|
+
while ((match = nodeRegex.exec(xml)) !== null) {
|
|
14
|
+
const block = match[1];
|
|
15
|
+
const type = block.match(/<OBJECT_TYPE>([^<]*)<\/OBJECT_TYPE>/)?.[1] ?? "";
|
|
16
|
+
const name = block.match(/<OBJECT_NAME>([^<]*)<\/OBJECT_NAME>/)?.[1] ?? "";
|
|
17
|
+
const uri = block.match(/<OBJECT_URI>([^<]*)<\/OBJECT_URI>/)?.[1] ?? "";
|
|
18
|
+
// Ignorar pacotes (DEVC/*) e entradas sem nome
|
|
19
|
+
if (!name || type.startsWith("DEVC/"))
|
|
20
|
+
continue;
|
|
21
|
+
objects.push({ name, type, uri });
|
|
22
|
+
}
|
|
23
|
+
return objects;
|
|
24
|
+
}
|
|
25
|
+
function formatPackageContents(packageName, objects) {
|
|
26
|
+
if (objects.length === 0) {
|
|
27
|
+
return `Pacote ${packageName}: vazio ou não encontrado.`;
|
|
28
|
+
}
|
|
29
|
+
const lines = [
|
|
30
|
+
`Pacote ${packageName} (${objects.length} objetos)`,
|
|
31
|
+
"─".repeat(70),
|
|
32
|
+
`${"Nome".padEnd(40)} Tipo`,
|
|
33
|
+
"─".repeat(70),
|
|
34
|
+
];
|
|
35
|
+
const sorted = [...objects].sort((a, b) => a.type.localeCompare(b.type) || a.name.localeCompare(b.name));
|
|
36
|
+
for (const obj of sorted) {
|
|
37
|
+
lines.push(`${obj.name.padEnd(40)} ${obj.type}`);
|
|
38
|
+
}
|
|
39
|
+
return lines.join("\n");
|
|
40
|
+
}
|
|
41
|
+
async function abapPackageContents(input) {
|
|
42
|
+
const { package_name } = input;
|
|
43
|
+
const name = package_name.toUpperCase();
|
|
44
|
+
const csrf = await (0, adt_client_js_1.ensureSession)();
|
|
45
|
+
const response = await adt_client_js_1.http.post("/repository/nodestructure", "", {
|
|
46
|
+
params: { parent_type: "DEVC/K", parent_name: name },
|
|
47
|
+
headers: {
|
|
48
|
+
Accept: "application/vnd.sap.as+xml",
|
|
49
|
+
"Content-Type": "text/plain",
|
|
50
|
+
"X-CSRF-Token": csrf,
|
|
51
|
+
},
|
|
52
|
+
responseType: "text",
|
|
53
|
+
validateStatus: (status) => status < 500,
|
|
54
|
+
});
|
|
55
|
+
if (response.status === 404) {
|
|
56
|
+
throw new Error(`Pacote ${name} não encontrado.`);
|
|
57
|
+
}
|
|
58
|
+
const objects = parsePackageContents(response.data);
|
|
59
|
+
return formatPackageContents(name, objects);
|
|
60
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapPrettyPrinter = abapPrettyPrinter;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapPrettyPrinter(input) {
|
|
6
|
+
const { source } = input;
|
|
7
|
+
const csrf = await (0, adt_client_js_1.ensureSession)();
|
|
8
|
+
// Tentar endpoint /abapsource/prettyprinter primeiro
|
|
9
|
+
let response = await adt_client_js_1.http.post("/abapsource/prettyprinter", source, {
|
|
10
|
+
headers: {
|
|
11
|
+
"Content-Type": "text/plain",
|
|
12
|
+
"Accept": "text/plain",
|
|
13
|
+
"X-CSRF-Token": csrf,
|
|
14
|
+
},
|
|
15
|
+
responseType: "text",
|
|
16
|
+
validateStatus: (status) => status < 500,
|
|
17
|
+
});
|
|
18
|
+
// Se 404, tentar variante sem 'er' no final
|
|
19
|
+
if (response.status === 404) {
|
|
20
|
+
response = await adt_client_js_1.http.post("/abapsource/prettyprint", source, {
|
|
21
|
+
headers: {
|
|
22
|
+
"Content-Type": "text/plain",
|
|
23
|
+
"Accept": "text/plain",
|
|
24
|
+
"X-CSRF-Token": csrf,
|
|
25
|
+
},
|
|
26
|
+
responseType: "text",
|
|
27
|
+
validateStatus: (status) => status < 500,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (response.status >= 300) {
|
|
31
|
+
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
32
|
+
throw new Error(`Pretty Printer falhou (HTTP ${response.status}): ${body.slice(0, 500)}`);
|
|
33
|
+
}
|
|
34
|
+
return response.data;
|
|
35
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapPublishBinding = abapPublishBinding;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
const system_profile_js_1 = require("../system-profile.js");
|
|
6
|
+
async function abapPublishBinding(input) {
|
|
7
|
+
const { binding_name, service_version = "0001" } = input;
|
|
8
|
+
const name = binding_name.toUpperCase();
|
|
9
|
+
const profile = (0, system_profile_js_1.getProfile)();
|
|
10
|
+
// Pre-flight: verificar se o mandante permite publish
|
|
11
|
+
if (!profile.client.allowLocalPublish) {
|
|
12
|
+
return `AVISO: Mandante configurado como "${profile.client.role}" — publish local pode não ser permitido. Tente assim mesmo ou altere a configuração do mandante no Gateway.`;
|
|
13
|
+
}
|
|
14
|
+
// BTP com auto-registration não precisa de publish
|
|
15
|
+
if (profile.gateway.autoRegistration) {
|
|
16
|
+
const odataUrl = (0, system_profile_js_1.resolveODataV4Url)(name, service_version);
|
|
17
|
+
return `Service Binding ${name} — publish não necessário (auto-registration ativo no BTP).\nOData URL: ${odataUrl}`;
|
|
18
|
+
}
|
|
19
|
+
const csrf = await (0, adt_client_js_1.ensureSession)();
|
|
20
|
+
const body = `<adtcore:objectReferences xmlns:adtcore="http://www.sap.com/adt/core">
|
|
21
|
+
<adtcore:objectReference adtcore:name="${name}"/>
|
|
22
|
+
</adtcore:objectReferences>`;
|
|
23
|
+
const response = await adt_client_js_1.http.post("/businessservices/odatav4/publishjobs", body, {
|
|
24
|
+
headers: {
|
|
25
|
+
"Accept": "application/*",
|
|
26
|
+
"Content-Type": "application/xml",
|
|
27
|
+
"X-CSRF-Token": csrf,
|
|
28
|
+
},
|
|
29
|
+
params: {
|
|
30
|
+
servicename: name,
|
|
31
|
+
serviceversion: service_version,
|
|
32
|
+
},
|
|
33
|
+
validateStatus: (status) => status >= 200 && status < 500,
|
|
34
|
+
});
|
|
35
|
+
if (response.status === 200 || response.status === 201) {
|
|
36
|
+
const raw = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
37
|
+
const severity = raw.match(/<SEVERITY[^>]*>([^<]*)<\/SEVERITY>/)?.[1] ?? "";
|
|
38
|
+
const shortText = raw.match(/<SHORT_TEXT[^>]*>([^<]*)<\/SHORT_TEXT>/)?.[1] ?? "";
|
|
39
|
+
const longText = raw.match(/<LONG_TEXT[^>]*>([^<]*)<\/LONG_TEXT>/)?.[1] ?? "";
|
|
40
|
+
const details = [shortText, longText].filter(Boolean).join("\n");
|
|
41
|
+
const odataUrl = (0, system_profile_js_1.resolveODataV4Url)(name, service_version);
|
|
42
|
+
if (severity && severity !== "S") {
|
|
43
|
+
return `Publish concluído com aviso (${severity}): ${details || "(sem detalhe)"}${odataUrl ? `\nOData URL: ${odataUrl}` : ""}`;
|
|
44
|
+
}
|
|
45
|
+
return `Service Binding ${name} publicada com sucesso.${details ? `\n${details}` : ""}${odataUrl ? `\nOData URL: ${odataUrl}` : ""}`;
|
|
46
|
+
}
|
|
47
|
+
const errorBody = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
48
|
+
throw new Error(`Erro ao publicar Service Binding (HTTP ${response.status}): ${errorBody.slice(0, 500)}`);
|
|
49
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapQuickFix = abapQuickFix;
|
|
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 abapQuickFix(input) {
|
|
14
|
+
const { object_type, object_name, line, column, fix_index, class_include, transport_request } = input;
|
|
15
|
+
const uri = resolveSourceUri(object_type, object_name, class_include);
|
|
16
|
+
if (fix_index === undefined || fix_index === null) {
|
|
17
|
+
// Listar quick fixes disponíveis
|
|
18
|
+
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
19
|
+
<quickfix:evaluation xmlns:quickfix="http://www.sap.com/adt/quickfixes">
|
|
20
|
+
<quickfix:context uri="${uri}" line="${line}" column="${column}"/>
|
|
21
|
+
</quickfix:evaluation>`;
|
|
22
|
+
const { data, status } = await (0, adt_client_js_1.adtPostXml)("/quickfixes/evaluation", body);
|
|
23
|
+
if (status >= 400) {
|
|
24
|
+
throw new Error(`Quick fix evaluation falhou (HTTP ${status}): ${data.slice(0, 500)}`);
|
|
25
|
+
}
|
|
26
|
+
// Parse fixes
|
|
27
|
+
const fixes = [];
|
|
28
|
+
const fixRegex = /<(?:quickfix:)?fix[^>]*>/gi;
|
|
29
|
+
let match;
|
|
30
|
+
let idx = 0;
|
|
31
|
+
while ((match = fixRegex.exec(data)) !== null) {
|
|
32
|
+
const el = match[0];
|
|
33
|
+
const descMatch = el.match(/description\s*=\s*"([^"]*)"/i);
|
|
34
|
+
const idxMatch = el.match(/index\s*=\s*"([^"]*)"/i);
|
|
35
|
+
fixes.push({
|
|
36
|
+
index: idxMatch ? parseInt(idxMatch[1], 10) : idx,
|
|
37
|
+
description: descMatch?.[1] ?? `Fix ${idx}`,
|
|
38
|
+
});
|
|
39
|
+
idx++;
|
|
40
|
+
}
|
|
41
|
+
if (fixes.length === 0) {
|
|
42
|
+
return `Nenhum quick fix disponível para ${object_name} na posição ${line}:${column}.`;
|
|
43
|
+
}
|
|
44
|
+
const lines = [];
|
|
45
|
+
lines.push(`Quick Fixes disponíveis para ${object_name} (linha ${line}, coluna ${column}):`);
|
|
46
|
+
lines.push("─".repeat(60));
|
|
47
|
+
for (const f of fixes) {
|
|
48
|
+
lines.push(` [${f.index}] ${f.description}`);
|
|
49
|
+
}
|
|
50
|
+
lines.push(`\nPara aplicar, chame novamente com fix_index=<número>.`);
|
|
51
|
+
return lines.join("\n");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Aplicar quick fix específico
|
|
55
|
+
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
56
|
+
<quickfix:application xmlns:quickfix="http://www.sap.com/adt/quickfixes">
|
|
57
|
+
<quickfix:fix index="${fix_index}"/>
|
|
58
|
+
<quickfix:context uri="${uri}" line="${line}" column="${column}"/>
|
|
59
|
+
</quickfix:application>`;
|
|
60
|
+
const params = {};
|
|
61
|
+
if (transport_request)
|
|
62
|
+
params.corrNr = transport_request;
|
|
63
|
+
const { data, status } = await (0, adt_client_js_1.adtPostXml)("/quickfixes/application", body, params);
|
|
64
|
+
if (status >= 400) {
|
|
65
|
+
throw new Error(`Quick fix application falhou (HTTP ${status}): ${data.slice(0, 500)}`);
|
|
66
|
+
}
|
|
67
|
+
return `Quick fix #${fix_index} aplicado com sucesso em ${object_name} (linha ${line}, coluna ${column}).`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapReadTool = void 0;
|
|
4
|
+
exports.abapRead = abapRead;
|
|
5
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
6
|
+
const object_versions_js_1 = require("./object-versions.js");
|
|
7
|
+
function resolveSourcePath(adtPath, name, objectType, classInclude) {
|
|
8
|
+
if (objectType === "CLAS/OC" && classInclude && classInclude !== "main") {
|
|
9
|
+
return `/${adtPath}/${name}/includes/${classInclude}/source/main`;
|
|
10
|
+
}
|
|
11
|
+
return `/${adtPath}/${name}/source/main`;
|
|
12
|
+
}
|
|
13
|
+
/** Tipos DDIC cujos campos vêm de metadados XML, não de /source/main */
|
|
14
|
+
const DDIC_METADATA_TYPES = ["TABL/DT", "TABL/DS", "TTYP/TT"];
|
|
15
|
+
function parseDdicFieldsFromXml(xml, objectName, objectType) {
|
|
16
|
+
const typeLabel = objectType === "TABL/DS" ? "Estrutura" : "Tabela transparente";
|
|
17
|
+
// Extrair campos dos elementos XML <field> ou <column>
|
|
18
|
+
const fields = [];
|
|
19
|
+
// Padrão ADT: campos aparecem como <field> com atributos ou sub-elementos
|
|
20
|
+
// Formato 1: <adtcore:field ... name="FIELD" type="CHAR" length="10" description="..." />
|
|
21
|
+
// Formato 2: <field adtcore:name="FIELD" adtcore:type="CHAR" .../>
|
|
22
|
+
// Formato 3: Elementos genéricos com name/type atributos
|
|
23
|
+
// Extrair todos os elementos que parecem campos
|
|
24
|
+
const fieldRegex = /<(?:[a-zA-Z0-9_:]+:)?(?:field|column|component)[^>]*?(?:adtcore:)?name\s*=\s*"([^"]*)"[^>]*?>/gi;
|
|
25
|
+
let match;
|
|
26
|
+
while ((match = fieldRegex.exec(xml)) !== null) {
|
|
27
|
+
const element = match[0];
|
|
28
|
+
const name = match[1];
|
|
29
|
+
const typeMatch = element.match(/(?:adtcore:)?type\s*=\s*"([^"]*)"/i) ||
|
|
30
|
+
element.match(/dataType\s*=\s*"([^"]*)"/i);
|
|
31
|
+
const lengthMatch = element.match(/(?:adtcore:)?length\s*=\s*"([^"]*)"/i);
|
|
32
|
+
const descMatch = element.match(/(?:adtcore:)?description\s*=\s*"([^"]*)"/i);
|
|
33
|
+
fields.push({
|
|
34
|
+
name,
|
|
35
|
+
type: typeMatch?.[1] ?? "",
|
|
36
|
+
length: lengthMatch?.[1] ?? "",
|
|
37
|
+
description: descMatch?.[1] ?? "",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Se não encontrou campos pelo regex acima, tentar abordagem mais genérica
|
|
41
|
+
// para elementos com atributos name que contenham info de tipo
|
|
42
|
+
if (fields.length === 0) {
|
|
43
|
+
// Tentar extrair de qualquer elemento com adtcore:name dentro de listas/coleções
|
|
44
|
+
const genericRegex = /<[^>]+?(?:adtcore:name|ddicName)\s*=\s*"([^"]+)"[^>]*?>/gi;
|
|
45
|
+
while ((match = genericRegex.exec(xml)) !== null) {
|
|
46
|
+
const element = match[0];
|
|
47
|
+
const name = match[1];
|
|
48
|
+
// Evitar duplicatas e elementos que não são campos (ex: o próprio objeto raiz)
|
|
49
|
+
if (name === objectName)
|
|
50
|
+
continue;
|
|
51
|
+
const typeMatch = element.match(/(?:ddicType|dataType|type)\s*=\s*"([^"]*)"/i);
|
|
52
|
+
const lengthMatch = element.match(/(?:ddicLength|length)\s*=\s*"([^"]*)"/i);
|
|
53
|
+
const descMatch = element.match(/(?:description)\s*=\s*"([^"]*)"/i);
|
|
54
|
+
fields.push({
|
|
55
|
+
name,
|
|
56
|
+
type: typeMatch?.[1] ?? "",
|
|
57
|
+
length: lengthMatch?.[1] ?? "",
|
|
58
|
+
description: descMatch?.[1] ?? "",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (fields.length > 0) {
|
|
63
|
+
const header = `${typeLabel}: ${objectName} (${fields.length} campos)\n${"─".repeat(60)}`;
|
|
64
|
+
const lines = fields.map((f) => {
|
|
65
|
+
const parts = [f.name];
|
|
66
|
+
if (f.type)
|
|
67
|
+
parts.push(f.type + (f.length ? `(${f.length})` : ""));
|
|
68
|
+
if (f.description)
|
|
69
|
+
parts.push(`— ${f.description}`);
|
|
70
|
+
return parts.join(" ");
|
|
71
|
+
});
|
|
72
|
+
return `${header}\n${lines.join("\n")}`;
|
|
73
|
+
}
|
|
74
|
+
// Fallback: retornar XML bruto formatado se não conseguimos parsear
|
|
75
|
+
return `${typeLabel}: ${objectName}\n\nMetadados XML (campos não puderam ser parseados automaticamente):\n\n${xml}`;
|
|
76
|
+
}
|
|
77
|
+
/** Busca resumo de versão do objeto (best-effort, não bloqueia em erro) */
|
|
78
|
+
async function getVersionSummary(objectType, objectName) {
|
|
79
|
+
try {
|
|
80
|
+
const result = await (0, object_versions_js_1.abapObjectVersions)({ object_type: objectType, object_name: objectName });
|
|
81
|
+
// Extrair info relevante do resultado formatado
|
|
82
|
+
const lines = result.split("\n").filter((l) => l.trim() && !l.startsWith("─") && !l.startsWith("Versão "));
|
|
83
|
+
const header = lines[0] || "";
|
|
84
|
+
// Pegar a versão mais recente (primeira após o header)
|
|
85
|
+
const latest = lines[1] || "";
|
|
86
|
+
if (!latest.trim())
|
|
87
|
+
return "";
|
|
88
|
+
// Contar versões do header: "Versões de NOME (N)"
|
|
89
|
+
const countMatch = header.match(/\((\d+)\)/);
|
|
90
|
+
const count = countMatch ? parseInt(countMatch[1]) : 0;
|
|
91
|
+
if (count <= 1)
|
|
92
|
+
return ""; // Só 1 versão = nunca foi modificado, sem risco
|
|
93
|
+
// Extrair autor e data da versão mais recente
|
|
94
|
+
const parts = latest.trim().split(/\s{2,}/);
|
|
95
|
+
const version = parts[0] || "";
|
|
96
|
+
const date = parts[1] || "";
|
|
97
|
+
const author = parts[2] || "";
|
|
98
|
+
return `\n\n⚠️ ATENÇÃO: Este objeto tem ${count} versões. Última modificação: ${date} por ${author} (versão ${version}).\n Verifique se a versão no DEV está alinhada com PRD antes de modificar (abap_object_versions para detalhes).`;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Silenciar — versões não disponíveis não deve impedir a leitura
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function abapRead(input) {
|
|
106
|
+
const { object_type, object_name, class_include } = input;
|
|
107
|
+
const name = object_name.toUpperCase();
|
|
108
|
+
const adtPath = (0, adt_client_js_1.resolveAdtPath)(object_type);
|
|
109
|
+
// Para TABL/DT, TABL/DS e TTYP/TT: tentar /source/main primeiro (CDS DDL), se 404 ler metadados XML
|
|
110
|
+
if (DDIC_METADATA_TYPES.includes(object_type)) {
|
|
111
|
+
const nameLower = name.toLowerCase();
|
|
112
|
+
const sourcePath = `/${adtPath}/${nameLower}/source/main`;
|
|
113
|
+
try {
|
|
114
|
+
const source = await (0, adt_client_js_1.adtGet)(sourcePath);
|
|
115
|
+
const versionInfo = await getVersionSummary(object_type, name);
|
|
116
|
+
return source + versionInfo;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const is404 = error instanceof Error && (error.message.includes("404") || error.message.includes("não encontrado"));
|
|
120
|
+
if (!is404)
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
// /source/main retornou 404 — ler metadados XML com campos
|
|
124
|
+
// Usar Accept header específico por tipo
|
|
125
|
+
const acceptMap = {
|
|
126
|
+
"TABL/DT": "application/vnd.sap.adt.tables.v2+xml",
|
|
127
|
+
"TABL/DS": "application/vnd.sap.adt.structures.v2+xml",
|
|
128
|
+
"TTYP/TT": "application/vnd.sap.adt.tabletype.v1+xml",
|
|
129
|
+
};
|
|
130
|
+
const metadataPath = `/${adtPath}/${nameLower}`;
|
|
131
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
132
|
+
const response = await adt_client_js_1.http.get(metadataPath, {
|
|
133
|
+
headers: { Accept: acceptMap[object_type] ?? "application/xml" },
|
|
134
|
+
responseType: "text",
|
|
135
|
+
validateStatus: (status) => status < 500,
|
|
136
|
+
});
|
|
137
|
+
if (response.status >= 400) {
|
|
138
|
+
throw new Error(`Objeto ${name} (${object_type}) não encontrado.`);
|
|
139
|
+
}
|
|
140
|
+
const xml = response.data;
|
|
141
|
+
return parseDdicFieldsFromXml(xml, name, object_type);
|
|
142
|
+
}
|
|
143
|
+
const sourcePath = resolveSourcePath(adtPath, name, object_type, class_include);
|
|
144
|
+
const source = await (0, adt_client_js_1.adtGet)(sourcePath);
|
|
145
|
+
// Apenas para objetos com source code (não DDIC metadados): incluir aviso de versão
|
|
146
|
+
const versionInfo = await getVersionSummary(object_type, name);
|
|
147
|
+
return source + versionInfo;
|
|
148
|
+
}
|
|
149
|
+
exports.abapReadTool = {
|
|
150
|
+
name: "abap_read",
|
|
151
|
+
description: "Lê o código-fonte de um objeto ABAP no sistema SAP via ADT API.",
|
|
152
|
+
inputSchema: {
|
|
153
|
+
type: "object",
|
|
154
|
+
properties: {
|
|
155
|
+
object_type: {
|
|
156
|
+
type: "string",
|
|
157
|
+
description: "Tipo do objeto SAP. Exemplos: PROG/P (report), CLAS/OC (classe), FUGR/FF (function module), DDLS/DF (CDS view), TABL/DT (tabela transparente), TABL/DS (estrutura), INTF/OI (interface).",
|
|
158
|
+
enum: ["PROG/P", "CLAS/OC", "FUGR/FF", "DOMA/D", "DTEL/D", "TABL/DT", "TABL/DS", "INTF/OI", "DDLS/DF", "DDLX/MX", "SRVD/SRV", "BDEF/BDO", "SRVB/SVB"],
|
|
159
|
+
},
|
|
160
|
+
object_name: {
|
|
161
|
+
type: "string",
|
|
162
|
+
description: "Nome do objeto ABAP (ex: ZR_SD_PEDIDOS_ABERTOS). Case insensitive.",
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
required: ["object_type", "object_name"],
|
|
166
|
+
},
|
|
167
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapRefactorRename = abapRefactorRename;
|
|
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 `/sap/bc/adt/${adtPath}/${objectName}/includes/${classInclude}/source/main`;
|
|
9
|
+
}
|
|
10
|
+
return `/sap/bc/adt/${adtPath}/${objectName}/source/main`;
|
|
11
|
+
}
|
|
12
|
+
function parseRenameResponse(xml) {
|
|
13
|
+
// Verificar se a resposta contém uma lista de alterações
|
|
14
|
+
const changes = [];
|
|
15
|
+
const changeRegex = /adtcore:uri\s*=\s*"([^"]*)"[^>]*?adtcore:name\s*=\s*"([^"]*)"/gi;
|
|
16
|
+
let match;
|
|
17
|
+
while ((match = changeRegex.exec(xml)) !== null) {
|
|
18
|
+
changes.push(` ${match[2]} (${match[1]})`);
|
|
19
|
+
}
|
|
20
|
+
if (changes.length > 0) {
|
|
21
|
+
return `Renomeação aplicada em ${changes.length} local(is):\n${changes.join("\n")}`;
|
|
22
|
+
}
|
|
23
|
+
// Tentar extrair mensagem de sucesso genérica
|
|
24
|
+
const msgMatch = xml.match(/<(?:message|shortText)[^>]*>([^<]*)<\//i);
|
|
25
|
+
if (msgMatch)
|
|
26
|
+
return msgMatch[1];
|
|
27
|
+
return "Renomeação aplicada com sucesso.";
|
|
28
|
+
}
|
|
29
|
+
async function abapRefactorRename(input) {
|
|
30
|
+
const { object_type, object_name, old_name, new_name, line, column, class_include } = input;
|
|
31
|
+
const name = object_name.toUpperCase();
|
|
32
|
+
const uri = resolveSourceUri(object_type, name, class_include);
|
|
33
|
+
const csrf = await (0, adt_client_js_1.ensureSession)();
|
|
34
|
+
// Payload XML para renomeação
|
|
35
|
+
const payload = `<?xml version="1.0" encoding="UTF-8"?>
|
|
36
|
+
<renaming:renameRefactoring xmlns:renaming="http://www.sap.com/adt/ris/renaming"
|
|
37
|
+
xmlns:adtcore="http://www.sap.com/adt/core">
|
|
38
|
+
<renaming:newName>${new_name}</renaming:newName>
|
|
39
|
+
<adtcore:objectReference adtcore:uri="${uri}" adtcore:name="${old_name}"/>
|
|
40
|
+
</renaming:renameRefactoring>`;
|
|
41
|
+
// Tentar execução direta
|
|
42
|
+
const response = await adt_client_js_1.http.post("/refactorings/rename", payload, {
|
|
43
|
+
headers: {
|
|
44
|
+
"Content-Type": "application/xml",
|
|
45
|
+
"Accept": "application/xml",
|
|
46
|
+
"X-CSRF-Token": csrf,
|
|
47
|
+
},
|
|
48
|
+
params: { uri, line: String(line), column: String(column) },
|
|
49
|
+
responseType: "text",
|
|
50
|
+
validateStatus: (status) => status < 500,
|
|
51
|
+
});
|
|
52
|
+
if (response.status >= 300) {
|
|
53
|
+
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
54
|
+
// Extrair mensagem de erro
|
|
55
|
+
const errMatch = body.match(/<(?:message|shortText|exception)[^>]*>([^<]*)<\//i);
|
|
56
|
+
const errorDetail = errMatch?.[1] ?? body.slice(0, 500);
|
|
57
|
+
throw new Error(`Renomeação falhou (HTTP ${response.status}): ${errorDetail}`);
|
|
58
|
+
}
|
|
59
|
+
return parseRenameResponse(response.data);
|
|
60
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapReleaseTransport = abapReleaseTransport;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapReleaseTransport(input) {
|
|
6
|
+
const { transport_request } = input;
|
|
7
|
+
const trId = transport_request.toUpperCase();
|
|
8
|
+
const csrf = await (0, adt_client_js_1.ensureSession)();
|
|
9
|
+
const response = await adt_client_js_1.http.post(`/cts/transportrequests/${trId}/newreleasejobs`, "", {
|
|
10
|
+
headers: {
|
|
11
|
+
"X-CSRF-Token": csrf,
|
|
12
|
+
"Accept": "application/xml",
|
|
13
|
+
},
|
|
14
|
+
validateStatus: (status) => status < 500,
|
|
15
|
+
});
|
|
16
|
+
if (response.status >= 200 && response.status < 300) {
|
|
17
|
+
return `Ordem de transporte ${trId} liberada com sucesso.`;
|
|
18
|
+
}
|
|
19
|
+
// Tentar extrair mensagem de erro do XML
|
|
20
|
+
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
21
|
+
const msgMatch = body.match(/<(?:message|shortText|exception)[^>]*>([^<]*)<\//i);
|
|
22
|
+
const errorDetail = msgMatch?.[1] ?? body.slice(0, 500);
|
|
23
|
+
throw new Error(`Falha ao liberar ${trId} (HTTP ${response.status}): ${errorDetail}`);
|
|
24
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapReleasedApis = abapReleasedApis;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapReleasedApis(input) {
|
|
6
|
+
const { filter, api_type = "all" } = input;
|
|
7
|
+
const params = {};
|
|
8
|
+
if (filter)
|
|
9
|
+
params.name = filter;
|
|
10
|
+
if (api_type && api_type !== "all") {
|
|
11
|
+
const typeMap = {
|
|
12
|
+
class: "CLAS/OC",
|
|
13
|
+
interface: "INTF/OI",
|
|
14
|
+
cds: "DDLS/DF",
|
|
15
|
+
};
|
|
16
|
+
if (typeMap[api_type])
|
|
17
|
+
params.type = typeMap[api_type];
|
|
18
|
+
}
|
|
19
|
+
params.releasedApi = "true";
|
|
20
|
+
const xml = await (0, adt_client_js_1.adtGetXmlWithParams)("/repository/informationsystem/search", {
|
|
21
|
+
...params,
|
|
22
|
+
operation: "quickSearch",
|
|
23
|
+
maxResults: "100",
|
|
24
|
+
});
|
|
25
|
+
// Parse results (same format as abap_search)
|
|
26
|
+
const results = [];
|
|
27
|
+
const refRegex = /adtcore:uri\s*=\s*"[^"]*"\s+adtcore:type\s*=\s*"([^"]+)"\s+adtcore:name\s*=\s*"([^"]+)"(?:\s+adtcore:packageName\s*=\s*"([^"]*)")?(?:[^>]*adtcore:description\s*=\s*"([^"]*)")?/gi;
|
|
28
|
+
let match;
|
|
29
|
+
while ((match = refRegex.exec(xml)) !== null) {
|
|
30
|
+
results.push({
|
|
31
|
+
type: match[1],
|
|
32
|
+
name: match[2],
|
|
33
|
+
packageName: match[3] ?? "",
|
|
34
|
+
description: match[4] ?? "",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (results.length === 0) {
|
|
38
|
+
return filter
|
|
39
|
+
? `Nenhuma API released encontrada com filtro "${filter}".`
|
|
40
|
+
: "Nenhuma API released retornada. O endpoint pode não estar disponível neste sistema.";
|
|
41
|
+
}
|
|
42
|
+
const lines = [];
|
|
43
|
+
lines.push(`APIs Released (C1 Contract)${filter ? ` — filtro: ${filter}` : ""} — ${results.length} encontradas`);
|
|
44
|
+
lines.push("═".repeat(70));
|
|
45
|
+
for (const r of results) {
|
|
46
|
+
const desc = r.description ? ` — ${r.description}` : "";
|
|
47
|
+
const pkg = r.packageName ? ` [${r.packageName}]` : "";
|
|
48
|
+
lines.push(` ${r.name} (${r.type})${pkg}${desc}`);
|
|
49
|
+
}
|
|
50
|
+
return lines.join("\n");
|
|
51
|
+
}
|