@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,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapSearchTool = void 0;
|
|
4
|
+
exports.abapSearch = abapSearch;
|
|
5
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
6
|
+
function parseSearchResponse(xml) {
|
|
7
|
+
const results = [];
|
|
8
|
+
const regex = /adtcore:uri="([^"]+)"\s+adtcore:type="([^"]+)"\s+adtcore:name="([^"]+)"(?:\s+adtcore:packageName="([^"]*)")?(?:[^>]*adtcore:description="([^"]*)")?/g;
|
|
9
|
+
let match;
|
|
10
|
+
while ((match = regex.exec(xml)) !== null) {
|
|
11
|
+
results.push({
|
|
12
|
+
uri: match[1],
|
|
13
|
+
type: match[2],
|
|
14
|
+
name: match[3],
|
|
15
|
+
package: match[4] ?? "",
|
|
16
|
+
description: match[5],
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return results;
|
|
20
|
+
}
|
|
21
|
+
function formatResults(results) {
|
|
22
|
+
if (results.length === 0)
|
|
23
|
+
return "Nenhum objeto encontrado.";
|
|
24
|
+
const lines = results.map((r) => {
|
|
25
|
+
const desc = r.description ? ` ${r.description}` : "";
|
|
26
|
+
return `${r.name.padEnd(40)} ${r.type.padEnd(10)} ${r.package}${desc}`;
|
|
27
|
+
});
|
|
28
|
+
const header = `${"Nome".padEnd(40)} ${"Tipo".padEnd(10)} Pacote`;
|
|
29
|
+
const separator = "-".repeat(80);
|
|
30
|
+
return [header, separator, ...lines].join("\n");
|
|
31
|
+
}
|
|
32
|
+
async function abapSearch(input) {
|
|
33
|
+
const { query, object_type, max_results = 20 } = input;
|
|
34
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
35
|
+
const params = {
|
|
36
|
+
operation: "quickSearch",
|
|
37
|
+
query: query.toUpperCase(),
|
|
38
|
+
maxResults: String(max_results),
|
|
39
|
+
};
|
|
40
|
+
if (object_type) {
|
|
41
|
+
params.objectType = object_type;
|
|
42
|
+
}
|
|
43
|
+
const response = await adt_client_js_1.http.get("/repository/informationsystem/search", {
|
|
44
|
+
params,
|
|
45
|
+
headers: { Accept: "application/xml" },
|
|
46
|
+
responseType: "text",
|
|
47
|
+
});
|
|
48
|
+
const results = parseSearchResponse(response.data);
|
|
49
|
+
return formatResults(results);
|
|
50
|
+
}
|
|
51
|
+
exports.abapSearchTool = {
|
|
52
|
+
name: "abap_search",
|
|
53
|
+
description: "Pesquisa objetos ABAP no sistema SAP por nome (suporta wildcards com *). Retorna nome, tipo e pacote.",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
query: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: "Termo de busca. Suporta wildcard (*). Exemplos: 'ZR_SD_*', 'ZTESTEDENIS', 'Z*PEDIDO*'.",
|
|
60
|
+
},
|
|
61
|
+
object_type: {
|
|
62
|
+
type: "string",
|
|
63
|
+
description: "Filtrar por tipo de objeto (opcional). Exemplos: PROG/P, CLAS/OC, TABL/DT.",
|
|
64
|
+
enum: ["PROG/P", "CLAS/OC", "FUGR/FF", "DOMA/D", "DTEL/D", "TABL/DT", "INTF/OI", "DDLS/DF", "DDLX/MX", "SRVD/SRV", "BDEF/BDO", "SRVB/SVB"],
|
|
65
|
+
},
|
|
66
|
+
max_results: {
|
|
67
|
+
type: "number",
|
|
68
|
+
description: "Número máximo de resultados (padrão: 20, máximo recomendado: 100).",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
required: ["query"],
|
|
72
|
+
},
|
|
73
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Funções compartilhadas para parsing e formatação de data preview XML.
|
|
4
|
+
* Usadas por data-preview.ts e sql-console.ts.
|
|
5
|
+
*
|
|
6
|
+
* O ADT retorna dados em formato column-based:
|
|
7
|
+
* <columns>
|
|
8
|
+
* <metadata name="COL1" description="Desc" type="C" .../>
|
|
9
|
+
* <dataSet>
|
|
10
|
+
* <data>val1</data>
|
|
11
|
+
* <data>val2</data>
|
|
12
|
+
* </dataSet>
|
|
13
|
+
* </columns>
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.parseDataPreview = parseDataPreview;
|
|
17
|
+
exports.formatDataPreview = formatDataPreview;
|
|
18
|
+
function parseDataPreview(xml) {
|
|
19
|
+
const columns = [];
|
|
20
|
+
const columnData = [];
|
|
21
|
+
// Total rows
|
|
22
|
+
const totalMatch = xml.match(/<dataPreview:totalRows>(\d+)<\/dataPreview:totalRows>/);
|
|
23
|
+
const totalRows = totalMatch ? parseInt(totalMatch[1], 10) : 0;
|
|
24
|
+
// Query time
|
|
25
|
+
const timeMatch = xml.match(/<dataPreview:queryExecutionTime>([^<]+)<\/dataPreview:queryExecutionTime>/);
|
|
26
|
+
const queryTime = timeMatch?.[1];
|
|
27
|
+
// Table name
|
|
28
|
+
const nameMatch = xml.match(/<dataPreview:name>([^<]+)<\/dataPreview:name>/);
|
|
29
|
+
const tableName = nameMatch?.[1];
|
|
30
|
+
// Parse each <dataPreview:columns> block
|
|
31
|
+
const colBlockRegex = /<dataPreview:columns>([\s\S]*?)<\/dataPreview:columns>/gi;
|
|
32
|
+
let match;
|
|
33
|
+
while ((match = colBlockRegex.exec(xml)) !== null) {
|
|
34
|
+
const block = match[1];
|
|
35
|
+
// Metadata
|
|
36
|
+
const metaMatch = block.match(/<dataPreview:metadata([^/]*)\/>/i);
|
|
37
|
+
if (metaMatch) {
|
|
38
|
+
const attrs = metaMatch[1];
|
|
39
|
+
const name = /dataPreview:name="([^"]*)"/.exec(attrs)?.[1] ?? "";
|
|
40
|
+
const desc = /dataPreview:description="([^"]*)"/.exec(attrs)?.[1] ?? "";
|
|
41
|
+
const type = /dataPreview:colType="([^"]*)"/.exec(attrs)?.[1]
|
|
42
|
+
?? /dataPreview:type="([^"]*)"/.exec(attrs)?.[1] ?? "";
|
|
43
|
+
const length = parseInt(/dataPreview:length="([^"]*)"/.exec(attrs)?.[1] ?? "0", 10);
|
|
44
|
+
const isKey = /dataPreview:keyAttribute="true"/.test(attrs);
|
|
45
|
+
columns.push({ name, description: desc, type, length, isKey });
|
|
46
|
+
}
|
|
47
|
+
// Data values for this column
|
|
48
|
+
const values = [];
|
|
49
|
+
const dataRegex = /<dataPreview:data\s*\/?>([^<]*)<\/dataPreview:data>|<dataPreview:data\s*\/>/gi;
|
|
50
|
+
let dataMatch;
|
|
51
|
+
// More robust: get all data tags including empty ones
|
|
52
|
+
const dataContent = block.match(/<dataPreview:dataSet>([\s\S]*?)<\/dataPreview:dataSet>/i)?.[1] ?? "";
|
|
53
|
+
const dataTagRegex = /<dataPreview:data\b[^>]*>([^<]*)<\/dataPreview:data>|<dataPreview:data\s*\/>/gi;
|
|
54
|
+
while ((dataMatch = dataTagRegex.exec(dataContent)) !== null) {
|
|
55
|
+
values.push(dataMatch[1]?.trim() ?? "");
|
|
56
|
+
}
|
|
57
|
+
columnData.push(values);
|
|
58
|
+
}
|
|
59
|
+
// Transpose column-based data to row-based
|
|
60
|
+
const numRows = columnData.length > 0 ? Math.max(...columnData.map((d) => d.length)) : 0;
|
|
61
|
+
const rows = [];
|
|
62
|
+
for (let r = 0; r < numRows; r++) {
|
|
63
|
+
const row = [];
|
|
64
|
+
for (let c = 0; c < columnData.length; c++) {
|
|
65
|
+
row.push(columnData[c][r] ?? "");
|
|
66
|
+
}
|
|
67
|
+
rows.push(row);
|
|
68
|
+
}
|
|
69
|
+
return { columns, rows, totalRows: numRows > 0 ? numRows : totalRows, queryTime, tableName };
|
|
70
|
+
}
|
|
71
|
+
function formatDataPreview(title, result) {
|
|
72
|
+
if (result.columns.length === 0 && result.rows.length === 0) {
|
|
73
|
+
return `${title}: nenhum dado retornado pelo data preview.`;
|
|
74
|
+
}
|
|
75
|
+
const lines = [];
|
|
76
|
+
const queryInfo = result.queryTime ? ` | Query: ${result.queryTime}s` : "";
|
|
77
|
+
lines.push(`Data Preview: ${result.tableName ?? title} (${result.totalRows} linhas${queryInfo})`);
|
|
78
|
+
lines.push("─".repeat(80));
|
|
79
|
+
if (result.columns.length > 0) {
|
|
80
|
+
const colNames = result.columns.map((c) => c.name);
|
|
81
|
+
const widths = colNames.map((col, i) => {
|
|
82
|
+
let max = col.length;
|
|
83
|
+
for (const row of result.rows) {
|
|
84
|
+
if (row[i] && row[i].length > max)
|
|
85
|
+
max = row[i].length;
|
|
86
|
+
}
|
|
87
|
+
return Math.min(max, 30);
|
|
88
|
+
});
|
|
89
|
+
const header = colNames.map((col, i) => col.padEnd(widths[i])).join(" | ");
|
|
90
|
+
lines.push(header);
|
|
91
|
+
lines.push(widths.map((w) => "─".repeat(w)).join("─┼─"));
|
|
92
|
+
for (const row of result.rows) {
|
|
93
|
+
const line = colNames.map((_, i) => {
|
|
94
|
+
const val = row[i] ?? "";
|
|
95
|
+
return val.substring(0, widths[i]).padEnd(widths[i]);
|
|
96
|
+
}).join(" | ");
|
|
97
|
+
lines.push(line);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return lines.join("\n");
|
|
101
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapSqlConsole = abapSqlConsole;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
const data_format_js_1 = require("./shared/data-format.js");
|
|
6
|
+
async function abapSqlConsole(input) {
|
|
7
|
+
const { sql, max_rows = 100 } = input;
|
|
8
|
+
const { data, status } = await (0, adt_client_js_1.adtPostText)("/datapreview/freestyle", sql, { rowNumber: String(max_rows) }, "application/vnd.sap.adt.datapreview.table.v1+xml");
|
|
9
|
+
if (status >= 400) {
|
|
10
|
+
// Extrair mensagem de erro do XML
|
|
11
|
+
const msgMatch = data.match(/<(?:message|exception)[^>]*>([\s\S]*?)<\/(?:message|exception)>/i);
|
|
12
|
+
const errorMsg = msgMatch?.[1]?.trim() ?? data.slice(0, 500);
|
|
13
|
+
throw new Error(`SQL Console erro (HTTP ${status}): ${errorMsg}`);
|
|
14
|
+
}
|
|
15
|
+
const result = (0, data_format_js_1.parseDataPreview)(data);
|
|
16
|
+
return (0, data_format_js_1.formatDataPreview)(`SQL Console (${max_rows} max rows)`, result);
|
|
17
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* abap_system_info — auto-detecta características do sistema SAP alvo.
|
|
4
|
+
*
|
|
5
|
+
* Probe via ADT REST APIs para determinar:
|
|
6
|
+
* - Tipo do sistema (S/4HANA on-prem, BTP, ECC)
|
|
7
|
+
* - Release e versão SAP_BASIS
|
|
8
|
+
* - Plataforma ABAP (Standard vs Cloud)
|
|
9
|
+
* - Topologia do gateway (Hub vs Embedded)
|
|
10
|
+
* - Tipo do mandante (Development vs Customizing)
|
|
11
|
+
* - Quirks (ETag, conversion exits, lock behavior)
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.abapSystemInfo = abapSystemInfo;
|
|
15
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
16
|
+
const system_profile_js_1 = require("../system-profile.js");
|
|
17
|
+
async function abapSystemInfo() {
|
|
18
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
19
|
+
const results = [];
|
|
20
|
+
results.push("=== SAP System Profile Detection ===\n");
|
|
21
|
+
// 1. Probe /sap/bc/adt/discovery para release e capabilities
|
|
22
|
+
let discoveryXml = "";
|
|
23
|
+
try {
|
|
24
|
+
const resp = await adt_client_js_1.http.get("/discovery", {
|
|
25
|
+
headers: { Accept: "application/atomsvc+xml" },
|
|
26
|
+
responseType: "text",
|
|
27
|
+
validateStatus: (s) => s < 500,
|
|
28
|
+
});
|
|
29
|
+
discoveryXml = resp.data;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
results.push("WARN: Não foi possível acessar /discovery");
|
|
33
|
+
}
|
|
34
|
+
// 2. Probe /sap/bc/adt/core/discovery para SID
|
|
35
|
+
let coreDiscoveryXml = "";
|
|
36
|
+
try {
|
|
37
|
+
const resp = await adt_client_js_1.http.get("/core/discovery", {
|
|
38
|
+
headers: { Accept: "application/xml" },
|
|
39
|
+
responseType: "text",
|
|
40
|
+
validateStatus: (s) => s < 500,
|
|
41
|
+
});
|
|
42
|
+
coreDiscoveryXml = resp.data;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Não disponível em todos os sistemas
|
|
46
|
+
}
|
|
47
|
+
// Extrair informações do discovery XML
|
|
48
|
+
const systemId = extractAttr(coreDiscoveryXml, "systemId") ||
|
|
49
|
+
extractAttr(discoveryXml, "systemId") || "";
|
|
50
|
+
const generatorVersion = extractAttr(discoveryXml, "version") || "";
|
|
51
|
+
results.push(`System ID: ${systemId || "(não detectado)"}`);
|
|
52
|
+
results.push(`ADT Version: ${generatorVersion || "(não detectado)"}`);
|
|
53
|
+
// Detectar SAP_BASIS version via heurística nas collections do discovery
|
|
54
|
+
const sapBasisVersion = detectBasisVersionFromCollections(discoveryXml, generatorVersion);
|
|
55
|
+
results.push(`SAP_BASIS: ${sapBasisVersion}`);
|
|
56
|
+
// Detectar plataforma ABAP (Cloud vs Standard)
|
|
57
|
+
const hasCloudCollections = discoveryXml.includes("abapLanguageVersion") ||
|
|
58
|
+
discoveryXml.includes("cloudDevelopment");
|
|
59
|
+
const abapPlatform = hasCloudCollections ? "CLOUD" : "STANDARD";
|
|
60
|
+
results.push(`ABAP Platform: ${abapPlatform}`);
|
|
61
|
+
// Detectar tipo do sistema
|
|
62
|
+
const systemType = detectSystemType(discoveryXml, sapBasisVersion, abapPlatform);
|
|
63
|
+
results.push(`System Type: ${systemType}`);
|
|
64
|
+
// Detectar release S/4HANA
|
|
65
|
+
const release = detectRelease(sapBasisVersion);
|
|
66
|
+
results.push(`Release: ${release}`);
|
|
67
|
+
// 3. Probe ETag behavior
|
|
68
|
+
const etagQuirk = await probeEtagBehavior();
|
|
69
|
+
results.push(`ETag requires manual fix: ${etagQuirk}`);
|
|
70
|
+
// 4. Probe client role via publish endpoint
|
|
71
|
+
const clientRole = await probeClientRole();
|
|
72
|
+
results.push(`Client Role: ${clientRole}`);
|
|
73
|
+
// 5. Probe lock behavior para DDLS
|
|
74
|
+
const lockQuirk = await probeLockBehavior();
|
|
75
|
+
results.push(`Lock returns 406 for DDLS: ${lockQuirk}`);
|
|
76
|
+
// Montar profile baseado no preset + detecções
|
|
77
|
+
const presetName = systemType === "BTP_ABAP_ENV" ? "BTP_ABAP_ENV" :
|
|
78
|
+
systemType === "ON_PREMISE_ECC" ? "ON_PREMISE_ECC" :
|
|
79
|
+
"ON_PREMISE_S4";
|
|
80
|
+
const profile = (0, system_profile_js_1.getPreset)(presetName);
|
|
81
|
+
// Sobrescrever com valores detectados
|
|
82
|
+
profile.system.systemId = systemId;
|
|
83
|
+
profile.system.release = release;
|
|
84
|
+
profile.system.sapBasisVersion = sapBasisVersion;
|
|
85
|
+
profile.system.abapPlatform = abapPlatform;
|
|
86
|
+
profile.system.type = systemType;
|
|
87
|
+
profile.client.role = clientRole;
|
|
88
|
+
profile.client.allowLocalPublish = clientRole === "DEVELOPMENT";
|
|
89
|
+
profile.quirks.etagRequiresManualFix = etagQuirk;
|
|
90
|
+
profile.quirks.lockReturns406ForDDLS = lockQuirk;
|
|
91
|
+
// Capabilities derivadas da versão
|
|
92
|
+
const basisNum = parseInt(sapBasisVersion, 10) || 0;
|
|
93
|
+
profile.capabilities.cdsViewEntity = basisNum >= 756;
|
|
94
|
+
profile.capabilities.cdsProjection = basisNum >= 756;
|
|
95
|
+
profile.capabilities.rapManaged = basisNum >= 754;
|
|
96
|
+
profile.capabilities.rapDraftEnabled = basisNum >= 757;
|
|
97
|
+
profile.capabilities.releasedApiOnly = abapPlatform === "CLOUD";
|
|
98
|
+
// Aplicar profile globalmente
|
|
99
|
+
(0, system_profile_js_1.setProfile)(profile);
|
|
100
|
+
results.push("\n=== Profile aplicado ===");
|
|
101
|
+
results.push(JSON.stringify(profile, null, 2));
|
|
102
|
+
return results.join("\n");
|
|
103
|
+
}
|
|
104
|
+
// ─── Probes ──────────────────────────────────────────────────────
|
|
105
|
+
async function probeEtagBehavior() {
|
|
106
|
+
// Tenta GET + PUT num programa standard para verificar ETag mismatch
|
|
107
|
+
try {
|
|
108
|
+
const getResp = await adt_client_js_1.http.get("/programs/programs/rs_about/source/main", {
|
|
109
|
+
headers: { Accept: "text/plain" },
|
|
110
|
+
responseType: "text",
|
|
111
|
+
validateStatus: (s) => s < 500,
|
|
112
|
+
});
|
|
113
|
+
if (getResp.status !== 200)
|
|
114
|
+
return false;
|
|
115
|
+
const etag = getResp.headers["etag"];
|
|
116
|
+
if (!etag)
|
|
117
|
+
return false;
|
|
118
|
+
// Verifica se o ETag é numérico puro (format novo) vs contém charset (format antigo)
|
|
119
|
+
// No on-premise, o GET retorna ETag com sufixo diferente do esperado pelo PUT
|
|
120
|
+
// Heurística: se o ETag é só dígitos e tem mais de 14 chars, provavelmente tem quirk
|
|
121
|
+
const isNumericOnly = /^\d+$/.test(etag);
|
|
122
|
+
return isNumericOnly && etag.length > 14;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function probeClientRole() {
|
|
129
|
+
try {
|
|
130
|
+
const csrf = await (0, adt_client_js_1.ensureSession)();
|
|
131
|
+
// Tenta publish de um binding que não existe — o erro revela o tipo do mandante
|
|
132
|
+
const resp = await adt_client_js_1.http.post("/businessservices/odatav4/publishjobs", `<?xml version="1.0" encoding="UTF-8"?>
|
|
133
|
+
<publish:publishRequest xmlns:publish="http://www.sap.com/adt/businessservices/odatav4">
|
|
134
|
+
<publish:serviceName>ZNONEXISTENT_PROBE_12345</publish:serviceName>
|
|
135
|
+
<publish:serviceVersion>0001</publish:serviceVersion>
|
|
136
|
+
</publish:publishRequest>`, {
|
|
137
|
+
headers: {
|
|
138
|
+
"Content-Type": "application/xml",
|
|
139
|
+
"X-CSRF-Token": csrf,
|
|
140
|
+
},
|
|
141
|
+
validateStatus: () => true,
|
|
142
|
+
});
|
|
143
|
+
const body = typeof resp.data === "string" ? resp.data : "";
|
|
144
|
+
if (body.includes("Customizing Client") || body.includes("customizing client")) {
|
|
145
|
+
return "CUSTOMIZING";
|
|
146
|
+
}
|
|
147
|
+
if (body.includes("not allowed") && body.includes("Production")) {
|
|
148
|
+
return "PRODUCTION";
|
|
149
|
+
}
|
|
150
|
+
// Se retornou erro de "not found" ou similar, o mandante permite publish
|
|
151
|
+
return "DEVELOPMENT";
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return "UNKNOWN";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function probeLockBehavior() {
|
|
158
|
+
try {
|
|
159
|
+
const csrf = await (0, adt_client_js_1.ensureSession)();
|
|
160
|
+
// Tenta lock de um objeto DDLS qualquer
|
|
161
|
+
const resp = await adt_client_js_1.http.post("/ddic/ddl/sources/i_businesspartner", null, {
|
|
162
|
+
headers: { "X-CSRF-Token": csrf },
|
|
163
|
+
params: { _action: "LOCK", accessMode: "MODIFY" },
|
|
164
|
+
validateStatus: () => true,
|
|
165
|
+
});
|
|
166
|
+
// Se retorna 406, confirma o quirk
|
|
167
|
+
if (resp.status === 406)
|
|
168
|
+
return true;
|
|
169
|
+
// Se conseguiu lock, faz unlock
|
|
170
|
+
if (resp.status < 300) {
|
|
171
|
+
await adt_client_js_1.http.post("/ddic/ddl/sources/i_businesspartner", null, {
|
|
172
|
+
headers: { "X-CSRF-Token": csrf },
|
|
173
|
+
params: { _action: "UNLOCK" },
|
|
174
|
+
validateStatus: () => true,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// ─── Helpers de parsing ──────────────────────────────────────────
|
|
184
|
+
function extractAttr(xml, attr) {
|
|
185
|
+
const regex = new RegExp(`${attr}="([^"]*)"`, "i");
|
|
186
|
+
const match = xml.match(regex);
|
|
187
|
+
return match?.[1] || "";
|
|
188
|
+
}
|
|
189
|
+
function extractBetween(xml, start, end) {
|
|
190
|
+
const si = xml.indexOf(start);
|
|
191
|
+
if (si < 0)
|
|
192
|
+
return "";
|
|
193
|
+
const ei = xml.indexOf(end, si + start.length);
|
|
194
|
+
if (ei < 0)
|
|
195
|
+
return "";
|
|
196
|
+
return xml.substring(si + start.length, ei).trim();
|
|
197
|
+
}
|
|
198
|
+
function detectBasisVersionFromCollections(discoveryXml, generatorVersion) {
|
|
199
|
+
// Tenta extrair do generator version (formato "SAP NetWeaver AS ABAP 758.0")
|
|
200
|
+
const vMatch = generatorVersion.match(/(\d{3,4})\.\d/);
|
|
201
|
+
if (vMatch)
|
|
202
|
+
return vMatch[1];
|
|
203
|
+
// Heurística baseada na presença de collections específicas no discovery XML
|
|
204
|
+
// Cada feature apareceu em uma versão específica do SAP_BASIS
|
|
205
|
+
const features = [
|
|
206
|
+
// [pattern no XML, versão mínima do BASIS]
|
|
207
|
+
["abapLanguageVersion", "758"], // ABAP Cloud/Steampunk (S/4 2023)
|
|
208
|
+
["behaviordefinitions/codecompletion", "757"], // RAP code completion (S/4 2022)
|
|
209
|
+
["behaviordefinitions", "754"], // RAP BDEF support (S/4 1909)
|
|
210
|
+
["ddic/ddlx/sources", "756"], // Metadata Extensions (S/4 2021)
|
|
211
|
+
["ddic/srvd/sources", "754"], // Service Definitions (S/4 1909)
|
|
212
|
+
["ServiceBindings", "754"], // Service Bindings
|
|
213
|
+
["ddic/ddl/sources", "750"], // CDS views (S/4 1511+)
|
|
214
|
+
["oo/classes", "700"], // Basic ABAP OO
|
|
215
|
+
];
|
|
216
|
+
let detectedVersion = "750"; // default conservador
|
|
217
|
+
for (const [pattern, version] of features) {
|
|
218
|
+
if (discoveryXml.includes(pattern)) {
|
|
219
|
+
const current = parseInt(detectedVersion, 10);
|
|
220
|
+
const candidate = parseInt(version, 10);
|
|
221
|
+
if (candidate > current) {
|
|
222
|
+
detectedVersion = version;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return detectedVersion;
|
|
227
|
+
}
|
|
228
|
+
function detectSystemType(discoveryXml, basisVersion, platform) {
|
|
229
|
+
if (platform === "CLOUD")
|
|
230
|
+
return "BTP_ABAP_ENV";
|
|
231
|
+
const bv = parseInt(basisVersion, 10) || 0;
|
|
232
|
+
// BW tem collections específicas
|
|
233
|
+
if (discoveryXml.includes("bw/modelingtools") || discoveryXml.includes("bw/querydesigner")) {
|
|
234
|
+
return "SAP_BW";
|
|
235
|
+
}
|
|
236
|
+
// S/4HANA = BASIS 750+ com certas capabilities
|
|
237
|
+
if (bv >= 750) {
|
|
238
|
+
// Verifica se tem RAP-related collections (mais provável S/4)
|
|
239
|
+
if (discoveryXml.includes("behaviordefinitions") || discoveryXml.includes("ServiceBindings")) {
|
|
240
|
+
return "ON_PREMISE_S4";
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// ECC ou sistema antigo
|
|
244
|
+
if (bv < 750)
|
|
245
|
+
return "ON_PREMISE_ECC";
|
|
246
|
+
// Default para on-premise S/4
|
|
247
|
+
return "ON_PREMISE_S4";
|
|
248
|
+
}
|
|
249
|
+
function detectRelease(basisVersion) {
|
|
250
|
+
const bv = parseInt(basisVersion, 10) || 0;
|
|
251
|
+
if (bv >= 758)
|
|
252
|
+
return "2023";
|
|
253
|
+
if (bv >= 757)
|
|
254
|
+
return "2022";
|
|
255
|
+
if (bv >= 756)
|
|
256
|
+
return "2021";
|
|
257
|
+
if (bv >= 755)
|
|
258
|
+
return "2020";
|
|
259
|
+
if (bv >= 754)
|
|
260
|
+
return "1909";
|
|
261
|
+
if (bv >= 753)
|
|
262
|
+
return "1809";
|
|
263
|
+
if (bv >= 752)
|
|
264
|
+
return "1709";
|
|
265
|
+
if (bv >= 751)
|
|
266
|
+
return "1610";
|
|
267
|
+
if (bv >= 750)
|
|
268
|
+
return "1511";
|
|
269
|
+
return basisVersion;
|
|
270
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapTraces = abapTraces;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapTraces(input) {
|
|
6
|
+
const { action = "list", object_type, object_name, user } = input;
|
|
7
|
+
if (action === "list") {
|
|
8
|
+
const xml = await (0, adt_client_js_1.adtGetXml)("/runtime/traces");
|
|
9
|
+
// Parse trace entries
|
|
10
|
+
const traces = [];
|
|
11
|
+
const traceRegex = /<(?:trace:)?(?:trace|entry)[^>]*>([\s\S]*?)<\/(?:trace:)?(?:trace|entry)>/gi;
|
|
12
|
+
let match;
|
|
13
|
+
while ((match = traceRegex.exec(xml)) !== null) {
|
|
14
|
+
const content = match[0];
|
|
15
|
+
const idMatch = content.match(/(?:trace:)?id\s*=\s*"([^"]*)"/i);
|
|
16
|
+
const dateMatch = content.match(/(?:trace:)?(?:date|timestamp)\s*=\s*"([^"]*)"/i);
|
|
17
|
+
const userMatch = content.match(/(?:trace:)?(?:user|owner)\s*=\s*"([^"]*)"/i);
|
|
18
|
+
const descMatch = content.match(/(?:trace:)?description\s*=\s*"([^"]*)"/i);
|
|
19
|
+
const statusMatch = content.match(/(?:trace:)?status\s*=\s*"([^"]*)"/i);
|
|
20
|
+
if (idMatch || dateMatch) {
|
|
21
|
+
traces.push({
|
|
22
|
+
id: idMatch?.[1] ?? "",
|
|
23
|
+
date: dateMatch?.[1] ?? "",
|
|
24
|
+
user: userMatch?.[1] ?? "",
|
|
25
|
+
description: descMatch?.[1] ?? "",
|
|
26
|
+
status: statusMatch?.[1] ?? "",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (traces.length === 0) {
|
|
31
|
+
return "Nenhum trace ABAP encontrado.";
|
|
32
|
+
}
|
|
33
|
+
const lines = [];
|
|
34
|
+
lines.push(`ABAP Traces (SAT): ${traces.length} trace(s)`);
|
|
35
|
+
lines.push("═".repeat(60));
|
|
36
|
+
for (const t of traces) {
|
|
37
|
+
const status = t.status ? ` [${t.status}]` : "";
|
|
38
|
+
lines.push(` ${t.id} | ${t.date} | ${t.user}${status}`);
|
|
39
|
+
if (t.description)
|
|
40
|
+
lines.push(` ${t.description}`);
|
|
41
|
+
}
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}
|
|
44
|
+
else if (action === "create") {
|
|
45
|
+
if (!object_type || !object_name) {
|
|
46
|
+
throw new Error("object_type e object_name são obrigatórios para criar um trace.");
|
|
47
|
+
}
|
|
48
|
+
const name = object_name.toUpperCase();
|
|
49
|
+
const adtPath = (0, adt_client_js_1.resolveAdtPath)(object_type);
|
|
50
|
+
const objectUri = `/sap/bc/adt/${adtPath}/${name}`;
|
|
51
|
+
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
52
|
+
<trace:traceConfiguration xmlns:trace="http://www.sap.com/adt/runtime/traces"
|
|
53
|
+
xmlns:adtcore="http://www.sap.com/adt/core">
|
|
54
|
+
<adtcore:objectReference adtcore:uri="${objectUri}" adtcore:name="${name}"/>
|
|
55
|
+
${user ? `<trace:user>${user}</trace:user>` : ""}
|
|
56
|
+
</trace:traceConfiguration>`;
|
|
57
|
+
const { data, status } = await (0, adt_client_js_1.adtPostXml)("/runtime/traces", body);
|
|
58
|
+
if (status >= 400) {
|
|
59
|
+
const msgMatch = data.match(/<(?:message|shortText)[^>]*>([\s\S]*?)<\/(?:message|shortText)>/i);
|
|
60
|
+
throw new Error(`Criação de trace falhou (HTTP ${status}): ${msgMatch?.[1] ?? data.slice(0, 500)}`);
|
|
61
|
+
}
|
|
62
|
+
return `Configuração de trace criada para ${name} (${object_type}).` +
|
|
63
|
+
(user ? `\nUsuário: ${user}` : "");
|
|
64
|
+
}
|
|
65
|
+
throw new Error(`Ação desconhecida: ${action}. Use "list" ou "create".`);
|
|
66
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapTransportContents = abapTransportContents;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapTransportContents(input) {
|
|
6
|
+
const { transport_request } = input;
|
|
7
|
+
const trId = transport_request.toUpperCase();
|
|
8
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
9
|
+
const response = await adt_client_js_1.http.get(`/cts/transportrequests/${trId}`, {
|
|
10
|
+
headers: { Accept: "application/vnd.sap.adt.transportorganizer.v1+xml" },
|
|
11
|
+
responseType: "text",
|
|
12
|
+
validateStatus: (status) => status < 500,
|
|
13
|
+
});
|
|
14
|
+
if (response.status >= 400) {
|
|
15
|
+
throw new Error(`Transporte ${trId} não encontrado (HTTP ${response.status}).`);
|
|
16
|
+
}
|
|
17
|
+
const xml = response.data;
|
|
18
|
+
// Parse transport header
|
|
19
|
+
const descMatch = xml.match(/(?:tm:)?desc(?:ription)?\s*=\s*"([^"]*)"/i);
|
|
20
|
+
const ownerMatch = xml.match(/(?:tm:)?owner\s*=\s*"([^"]*)"/i);
|
|
21
|
+
const targetMatch = xml.match(/(?:tm:)?target(?:System)?\s*=\s*"([^"]*)"/i);
|
|
22
|
+
const statusMatch = xml.match(/(?:tm:)?status\s*=\s*"([^"]*)"/i);
|
|
23
|
+
// Parse objects
|
|
24
|
+
const objects = [];
|
|
25
|
+
const objRegex = /<(?:tm:)?(?:abapObject|object)[^>]*>/gi;
|
|
26
|
+
let match;
|
|
27
|
+
while ((match = objRegex.exec(xml)) !== null) {
|
|
28
|
+
const el = match[0];
|
|
29
|
+
const typeMatch = el.match(/(?:adtcore:type|tm:type|type)\s*=\s*"([^"]*)"/i);
|
|
30
|
+
const nameMatch = el.match(/(?:adtcore:name|tm:name|tm:objName|name)\s*=\s*"([^"]*)"/i);
|
|
31
|
+
const pgmidMatch = el.match(/(?:tm:pgmid|pgmid)\s*=\s*"([^"]*)"/i);
|
|
32
|
+
if (nameMatch) {
|
|
33
|
+
objects.push({
|
|
34
|
+
pgmid: pgmidMatch?.[1] ?? "R3TR",
|
|
35
|
+
type: typeMatch?.[1] ?? "",
|
|
36
|
+
name: nameMatch[1],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Parse tasks
|
|
41
|
+
const tasks = [];
|
|
42
|
+
const taskRegex = /<(?:tm:)?task[^>]*>/gi;
|
|
43
|
+
while ((match = taskRegex.exec(xml)) !== null) {
|
|
44
|
+
const el = match[0];
|
|
45
|
+
const idMatch = el.match(/(?:tm:)?(?:number|id)\s*=\s*"([^"]*)"/i);
|
|
46
|
+
const tOwner = el.match(/(?:tm:)?owner\s*=\s*"([^"]*)"/i);
|
|
47
|
+
const tDesc = el.match(/(?:tm:)?desc(?:ription)?\s*=\s*"([^"]*)"/i);
|
|
48
|
+
if (idMatch) {
|
|
49
|
+
tasks.push({
|
|
50
|
+
id: idMatch[1],
|
|
51
|
+
owner: tOwner?.[1] ?? "",
|
|
52
|
+
desc: tDesc?.[1] ?? "",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const lines = [];
|
|
57
|
+
lines.push(`Transporte: ${trId}`);
|
|
58
|
+
lines.push("═".repeat(60));
|
|
59
|
+
if (descMatch)
|
|
60
|
+
lines.push(`Descrição: ${descMatch[1]}`);
|
|
61
|
+
if (ownerMatch)
|
|
62
|
+
lines.push(`Owner: ${ownerMatch[1]}`);
|
|
63
|
+
if (targetMatch)
|
|
64
|
+
lines.push(`Destino: ${targetMatch[1]}`);
|
|
65
|
+
if (statusMatch)
|
|
66
|
+
lines.push(`Status: ${statusMatch[1]}`);
|
|
67
|
+
if (tasks.length > 0) {
|
|
68
|
+
lines.push(`\nTasks (${tasks.length}):`);
|
|
69
|
+
for (const t of tasks) {
|
|
70
|
+
lines.push(` ${t.id} — ${t.owner} — ${t.desc}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (objects.length > 0) {
|
|
74
|
+
lines.push(`\nObjetos (${objects.length}):`);
|
|
75
|
+
for (const o of objects) {
|
|
76
|
+
lines.push(` ${o.pgmid} ${o.type} ${o.name}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
lines.push(`\nNenhum objeto na ordem.`);
|
|
81
|
+
}
|
|
82
|
+
return lines.join("\n");
|
|
83
|
+
}
|