@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.
Files changed (114) hide show
  1. package/README.md +384 -0
  2. package/dist/adt-client.js +364 -0
  3. package/dist/cli/activate.js +113 -0
  4. package/dist/cli/init.js +333 -0
  5. package/dist/cli/remove.js +80 -0
  6. package/dist/cli/status.js +229 -0
  7. package/dist/cli/systems.js +68 -0
  8. package/dist/cli.js +81 -0
  9. package/dist/index.js +1318 -0
  10. package/dist/knowledge/abap/abap-dictionary.md +199 -0
  11. package/dist/knowledge/abap/abap-sql.md +296 -0
  12. package/dist/knowledge/abap/amdp.md +273 -0
  13. package/dist/knowledge/abap/clean-code.md +293 -0
  14. package/dist/knowledge/abap/cloud-background-processing.md +250 -0
  15. package/dist/knowledge/abap/cloud-communication.md +265 -0
  16. package/dist/knowledge/abap/cloud-development.md +176 -0
  17. package/dist/knowledge/abap/cloud-extensibility.md +252 -0
  18. package/dist/knowledge/abap/cloud-released-apis.md +261 -0
  19. package/dist/knowledge/abap/constructor-expressions.md +289 -0
  20. package/dist/knowledge/abap/enhancements.md +232 -0
  21. package/dist/knowledge/abap/exceptions.md +271 -0
  22. package/dist/knowledge/abap/internal-tables.md +205 -0
  23. package/dist/knowledge/abap/object-orientation.md +298 -0
  24. package/dist/knowledge/abap/performance.md +216 -0
  25. package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
  26. package/dist/knowledge/abap/rap-business-events.md +216 -0
  27. package/dist/knowledge/abap/rap-draft.md +191 -0
  28. package/dist/knowledge/abap/rap-eml.md +453 -0
  29. package/dist/knowledge/abap/rap-end-to-end.md +486 -0
  30. package/dist/knowledge/abap/rap-feature-control.md +185 -0
  31. package/dist/knowledge/abap/rap-numbering.md +280 -0
  32. package/dist/knowledge/abap/rap-service-exposure.md +163 -0
  33. package/dist/knowledge/abap/rap-unmanaged.md +468 -0
  34. package/dist/knowledge/abap/string-processing.md +180 -0
  35. package/dist/knowledge/abap/unit-testing.md +303 -0
  36. package/dist/knowledge/abap-cds/access-control.md +241 -0
  37. package/dist/knowledge/abap-cds/annotations.md +331 -0
  38. package/dist/knowledge/abap-cds/associations.md +254 -0
  39. package/dist/knowledge/abap-cds/expressions.md +230 -0
  40. package/dist/knowledge/abap-cds/functions.md +245 -0
  41. package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
  42. package/dist/knowledge/cap/authentication.md +278 -0
  43. package/dist/knowledge/cap/cdl-syntax.md +247 -0
  44. package/dist/knowledge/cap/cql-queries.md +266 -0
  45. package/dist/knowledge/cap/deployment.md +343 -0
  46. package/dist/knowledge/cap/event-handlers.md +287 -0
  47. package/dist/knowledge/cap/fiori-integration.md +303 -0
  48. package/dist/knowledge/cap/service-definitions.md +287 -0
  49. package/dist/knowledge/fiori/annotations.md +347 -0
  50. package/dist/knowledge/fiori/deployment.md +340 -0
  51. package/dist/knowledge/fiori/fiori-elements.md +332 -0
  52. package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
  53. package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
  54. package/dist/knowledge/fiori/ui5-controllers.md +358 -0
  55. package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
  56. package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
  57. package/dist/knowledge/fiori/ui5-manifest.md +411 -0
  58. package/dist/knowledge/fiori/ui5-routing.md +303 -0
  59. package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
  60. package/dist/logger.js +114 -0
  61. package/dist/system-profile.js +207 -0
  62. package/dist/tools/abap-doc.js +72 -0
  63. package/dist/tools/abapgit.js +161 -0
  64. package/dist/tools/activate.js +68 -0
  65. package/dist/tools/atc-check.js +117 -0
  66. package/dist/tools/auth-object.js +56 -0
  67. package/dist/tools/breakpoints.js +76 -0
  68. package/dist/tools/call-hierarchy.js +84 -0
  69. package/dist/tools/cds-annotations.js +98 -0
  70. package/dist/tools/cds-dependencies.js +65 -0
  71. package/dist/tools/check.js +47 -0
  72. package/dist/tools/code-completion.js +70 -0
  73. package/dist/tools/code-coverage.js +111 -0
  74. package/dist/tools/create-amdp.js +111 -0
  75. package/dist/tools/create-dcl.js +81 -0
  76. package/dist/tools/create-transport.js +38 -0
  77. package/dist/tools/create.js +285 -0
  78. package/dist/tools/data-preview.js +37 -0
  79. package/dist/tools/delete.js +45 -0
  80. package/dist/tools/deploy-bsp.js +298 -0
  81. package/dist/tools/discovery.js +59 -0
  82. package/dist/tools/element-info.js +93 -0
  83. package/dist/tools/enhancements.js +186 -0
  84. package/dist/tools/extract-method.js +44 -0
  85. package/dist/tools/function-group.js +59 -0
  86. package/dist/tools/knowledge.js +275 -0
  87. package/dist/tools/lock-object.js +75 -0
  88. package/dist/tools/message-class.js +67 -0
  89. package/dist/tools/navigate.js +80 -0
  90. package/dist/tools/number-range.js +57 -0
  91. package/dist/tools/object-documentation.js +43 -0
  92. package/dist/tools/object-structure.js +78 -0
  93. package/dist/tools/object-versions.js +57 -0
  94. package/dist/tools/package-contents.js +60 -0
  95. package/dist/tools/pretty-printer.js +35 -0
  96. package/dist/tools/publish-binding.js +49 -0
  97. package/dist/tools/quick-fix.js +69 -0
  98. package/dist/tools/read.js +167 -0
  99. package/dist/tools/refactor-rename.js +60 -0
  100. package/dist/tools/release-transport.js +24 -0
  101. package/dist/tools/released-apis.js +51 -0
  102. package/dist/tools/repository-tree.js +90 -0
  103. package/dist/tools/scaffold-rap.js +642 -0
  104. package/dist/tools/search.js +73 -0
  105. package/dist/tools/shared/data-format.js +101 -0
  106. package/dist/tools/sql-console.js +17 -0
  107. package/dist/tools/system-info.js +270 -0
  108. package/dist/tools/traces.js +66 -0
  109. package/dist/tools/transport-contents.js +83 -0
  110. package/dist/tools/transports.js +67 -0
  111. package/dist/tools/unit-test.js +135 -0
  112. package/dist/tools/where-used.js +59 -0
  113. package/dist/tools/write.js +101 -0
  114. package/package.json +49 -0
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCdsDependencies = abapCdsDependencies;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ async function abapCdsDependencies(input) {
6
+ const { object_name, direction = "both" } = input;
7
+ const name = object_name.toUpperCase();
8
+ const adtPath = (0, adt_client_js_1.resolveAdtPath)("DDLS/DF");
9
+ const uri = `/sap/bc/adt/${adtPath}/${name}`;
10
+ const results = [];
11
+ if (direction === "uses" || direction === "both") {
12
+ // O que essa CDS consome
13
+ const xml = await (0, adt_client_js_1.adtGetXmlWithParams)("/repository/informationsystem/objectreferences", {
14
+ uri,
15
+ direction: "uses",
16
+ });
17
+ const refs = parseReferences(xml);
18
+ results.push({ dir: "Dependências (o que consome)", refs });
19
+ }
20
+ if (direction === "used_by" || direction === "both") {
21
+ // Quem consome essa CDS
22
+ const xml = await (0, adt_client_js_1.adtGetXmlWithParams)("/repository/informationsystem/objectreferences", {
23
+ uri,
24
+ direction: "usedBy",
25
+ });
26
+ const refs = parseReferences(xml);
27
+ results.push({ dir: "Dependentes (quem consome)", refs });
28
+ }
29
+ const lines = [];
30
+ lines.push(`Dependências CDS: ${name}`);
31
+ lines.push("═".repeat(60));
32
+ for (const r of results) {
33
+ lines.push(`\n${r.dir}: ${r.refs.length} referência(s)`);
34
+ lines.push("─".repeat(50));
35
+ if (r.refs.length === 0) {
36
+ lines.push(" (nenhuma)");
37
+ }
38
+ else {
39
+ for (const ref of r.refs) {
40
+ const desc = ref.description ? ` — ${ref.description}` : "";
41
+ lines.push(` ${ref.name} [${ref.type}]${desc}`);
42
+ }
43
+ }
44
+ }
45
+ return lines.join("\n");
46
+ }
47
+ function parseReferences(xml) {
48
+ const refs = [];
49
+ const refRegex = /<(?:adtcore:)?objectReference[^>]*>/gi;
50
+ let match;
51
+ while ((match = refRegex.exec(xml)) !== null) {
52
+ const el = match[0];
53
+ const nameMatch = el.match(/adtcore:name\s*=\s*"([^"]*)"/i);
54
+ const typeMatch = el.match(/adtcore:type\s*=\s*"([^"]*)"/i);
55
+ const descMatch = el.match(/adtcore:description\s*=\s*"([^"]*)"/i);
56
+ if (nameMatch) {
57
+ refs.push({
58
+ name: nameMatch[1],
59
+ type: typeMatch?.[1] ?? "",
60
+ description: descMatch?.[1] ?? "",
61
+ });
62
+ }
63
+ }
64
+ return refs;
65
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCheck = abapCheck;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ function parseCheckResponse(xml) {
6
+ const messages = [];
7
+ const msgRegex = /<chkl:message[^>]*type="([^"]*)"[^>]*>[\s\S]*?<chkl:shortText[^>]*>([\s\S]*?)<\/chkl:shortText>[\s\S]*?(?:<chkl:uri[^>]*line="(\d+)")?/g;
8
+ let match;
9
+ while ((match = msgRegex.exec(xml)) !== null) {
10
+ messages.push({ type: match[1], text: match[2].trim(), line: match[3] });
11
+ }
12
+ const hasErrors = messages.some((m) => m.type === "E" || m.type === "A");
13
+ return { hasErrors, messages };
14
+ }
15
+ async function abapCheck(input) {
16
+ const { object_type, object_name } = input;
17
+ const adtPath = (0, adt_client_js_1.resolveAdtPath)(object_type);
18
+ const objectUri = `/sap/bc/adt/${adtPath}/${object_name.toUpperCase()}`;
19
+ const csrf = await (0, adt_client_js_1.ensureSession)();
20
+ const payload = `<?xml version="1.0" encoding="UTF-8"?>
21
+ <adtcore:objectReferences xmlns:adtcore="http://www.sap.com/adt/core">
22
+ <adtcore:objectReference adtcore:uri="${objectUri}" adtcore:type="${object_type}" adtcore:name="${object_name.toUpperCase()}"/>
23
+ </adtcore:objectReferences>`;
24
+ const response = await adt_client_js_1.http.post("/activation", payload, {
25
+ headers: {
26
+ "Content-Type": "application/xml",
27
+ "X-CSRF-Token": csrf,
28
+ },
29
+ params: { method: "activate", preauditRequested: "true" },
30
+ responseType: "text",
31
+ // Aceita 200 (check OK) e 400 (erros de sintaxe)
32
+ validateStatus: (s) => s === 200 || s === 400,
33
+ });
34
+ const { hasErrors, messages } = parseCheckResponse(response.data);
35
+ if (messages.length === 0) {
36
+ return `${object_name.toUpperCase()}: sem erros de sintaxe. (Objeto foi ativado se estava inativo.)`;
37
+ }
38
+ const lines = messages.map((m) => {
39
+ const prefix = m.type === "E" || m.type === "A" ? "ERRO" : m.type === "W" ? "AVISO" : "INFO";
40
+ const loc = m.line ? ` (linha ${m.line})` : "";
41
+ return ` [${prefix}]${loc} ${m.text}`;
42
+ });
43
+ if (hasErrors) {
44
+ return `${object_name.toUpperCase()}: ${messages.filter((m) => m.type === "E" || m.type === "A").length} erro(s) encontrado(s):\n${lines.join("\n")}`;
45
+ }
46
+ return `${object_name.toUpperCase()}: sem erros (${messages.length} aviso(s)):\n${lines.join("\n")}`;
47
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCodeCompletion = abapCodeCompletion;
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.toLowerCase();
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 abapCodeCompletion(input) {
14
+ const { object_type, object_name, line, column, class_include } = input;
15
+ const uri = resolveSourceUri(object_type, object_name, class_include);
16
+ const csrf = await (0, adt_client_js_1.ensureSession)();
17
+ const response = await adt_client_js_1.http.post("/abapsource/codecompletion/proposal", "", {
18
+ params: { uri, line: String(line), column: String(column) },
19
+ headers: {
20
+ Accept: "application/vnd.sap.as+xml",
21
+ "Content-Type": "text/plain",
22
+ "X-CSRF-Token": csrf,
23
+ },
24
+ responseType: "text",
25
+ validateStatus: (status) => status < 500,
26
+ });
27
+ if (response.status >= 400) {
28
+ const msg = typeof response.data === "string"
29
+ ? (response.data.match(/<message[^>]*>([^<]+)<\/message>/i)?.[1] ?? `HTTP ${response.status}`)
30
+ : `HTTP ${response.status}`;
31
+ throw new Error(msg);
32
+ }
33
+ const xml = response.data;
34
+ if (!xml || xml.trim().length === 0) {
35
+ return `Nenhuma sugestão de code completion para ${object_name} na posição ${line}:${column}.`;
36
+ }
37
+ // Parse proposals - formato SAP ABAP XML
38
+ const proposals = [];
39
+ // Pattern: <PROPOSALS><PROPOSAL><IDENTIFIER>name</IDENTIFIER>...</PROPOSAL>
40
+ const propRegex = /<PROPOSAL>([\s\S]*?)<\/PROPOSAL>/gi;
41
+ let match;
42
+ while ((match = propRegex.exec(xml)) !== null) {
43
+ const block = match[1];
44
+ const id = block.match(/<IDENTIFIER>([^<]*)<\/IDENTIFIER>/)?.[1] ?? "";
45
+ const desc = block.match(/<DESCRIPTION>([^<]*)<\/DESCRIPTION>/)?.[1] ?? "";
46
+ if (id)
47
+ proposals.push({ name: id, description: desc });
48
+ }
49
+ // Fallback: formato XML attributes
50
+ if (proposals.length === 0) {
51
+ const attrRegex = /(?:name|identifier)\s*=\s*"([^"]*)"/gi;
52
+ while ((match = attrRegex.exec(xml)) !== null) {
53
+ proposals.push({ name: match[1], description: "" });
54
+ }
55
+ }
56
+ if (proposals.length === 0) {
57
+ return `Nenhuma sugestão de code completion para ${object_name} na posição ${line}:${column}.`;
58
+ }
59
+ const lines = [];
60
+ lines.push(`Code Completion: ${object_name} (linha ${line}, coluna ${column}) — ${proposals.length} sugestões`);
61
+ lines.push("─".repeat(80));
62
+ for (const p of proposals.slice(0, 50)) {
63
+ const desc = p.description ? ` — ${p.description}` : "";
64
+ lines.push(` ${p.name}${desc}`);
65
+ }
66
+ if (proposals.length > 50) {
67
+ lines.push(` ... +${proposals.length - 50} sugestões omitidas`);
68
+ }
69
+ return lines.join("\n");
70
+ }
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCodeCoverage = abapCodeCoverage;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ async function abapCodeCoverage(input) {
6
+ const { object_type, object_name } = input;
7
+ const name = object_name.toUpperCase();
8
+ const adtPath = (0, adt_client_js_1.resolveAdtPath)(object_type);
9
+ const objectUri = `/sap/bc/adt/${adtPath}/${name}`;
10
+ const body = `<?xml version="1.0" encoding="UTF-8"?>
11
+ <aunit:runConfiguration xmlns:aunit="http://www.sap.com/adt/aunit">
12
+ <external>
13
+ <coverage active="true">
14
+ <format>statement</format>
15
+ </coverage>
16
+ </external>
17
+ <options>
18
+ <uriType value="semantic"/>
19
+ <testDeterminationStrategy sameProgram="true" assignedTests="false" publicApi="false"/>
20
+ <testRiskLevels harmless="true" dangerous="true" critical="true"/>
21
+ <testDurations short="true" medium="true" long="true"/>
22
+ </options>
23
+ <adtcore:objectSets xmlns:adtcore="http://www.sap.com/adt/core">
24
+ <objectSet kind="inclusive">
25
+ <adtcore:objectReferences>
26
+ <adtcore:objectReference adtcore:uri="${objectUri}" adtcore:name="${name}"/>
27
+ </adtcore:objectReferences>
28
+ </objectSet>
29
+ </adtcore:objectSets>
30
+ </aunit:runConfiguration>`;
31
+ const { data, status } = await (0, adt_client_js_1.adtPostXml)("/abapunit/testruns", body);
32
+ if (status >= 400) {
33
+ throw new Error(`Code coverage falhou (HTTP ${status}): ${data.slice(0, 500)}`);
34
+ }
35
+ // Parse test results
36
+ const testResults = [];
37
+ const classRegex = /<testClass[^>]*adtcore:name\s*=\s*"([^"]*)"[^>]*>([\s\S]*?)<\/testClass>/gi;
38
+ let classMatch;
39
+ while ((classMatch = classRegex.exec(data)) !== null) {
40
+ const className = classMatch[1];
41
+ const classContent = classMatch[2];
42
+ const methodRegex = /<testMethod[^>]*adtcore:name\s*=\s*"([^"]*)"[^>]*>([\s\S]*?)<\/testMethod>/gi;
43
+ let methodMatch;
44
+ while ((methodMatch = methodRegex.exec(classContent)) !== null) {
45
+ const methodName = methodMatch[1];
46
+ const methodContent = methodMatch[2];
47
+ const hasAlert = /<alert/i.test(methodContent);
48
+ const alertKind = methodContent.match(/kind\s*=\s*"([^"]*)"/i);
49
+ let mStatus = "PASSED";
50
+ if (hasAlert) {
51
+ if (alertKind && /fail|exception|error/i.test(alertKind[1])) {
52
+ mStatus = "FAILED";
53
+ }
54
+ else {
55
+ mStatus = "WARNING";
56
+ }
57
+ }
58
+ testResults.push({ className, method: methodName, status: mStatus });
59
+ }
60
+ }
61
+ // Parse coverage data
62
+ const coverageLines = [];
63
+ const stmtRegex = /<statement[^>]*?(?:executed|covered)\s*=\s*"([^"]*)"[^>]*?line\s*=\s*"([^"]*)"[^>]*>/gi;
64
+ let stmtMatch;
65
+ while ((stmtMatch = stmtRegex.exec(data)) !== null) {
66
+ coverageLines.push({
67
+ line: parseInt(stmtMatch[2], 10),
68
+ executed: stmtMatch[1] === "true" || stmtMatch[1] === "1",
69
+ });
70
+ }
71
+ // Fallback: try alternative coverage format
72
+ if (coverageLines.length === 0) {
73
+ const covRegex = /<(?:coverage:)?line[^>]*?nr\s*=\s*"([^"]*)"[^>]*?executed\s*=\s*"([^"]*)"[^>]*>/gi;
74
+ while ((stmtMatch = covRegex.exec(data)) !== null) {
75
+ coverageLines.push({
76
+ line: parseInt(stmtMatch[1], 10),
77
+ executed: stmtMatch[2] === "true" || stmtMatch[2] === "X",
78
+ });
79
+ }
80
+ }
81
+ // Format output
82
+ const lines = [];
83
+ lines.push(`Code Coverage: ${name}`);
84
+ lines.push("═".repeat(60));
85
+ // Test results summary
86
+ const passed = testResults.filter(t => t.status === "PASSED").length;
87
+ const failed = testResults.filter(t => t.status === "FAILED").length;
88
+ lines.push(`\nTestes: ${passed} passed, ${failed} failed, ${testResults.length} total`);
89
+ for (const t of testResults) {
90
+ const icon = t.status === "PASSED" ? "OK" : t.status === "FAILED" ? "FAIL" : "WARN";
91
+ lines.push(` [${icon}] ${t.className}->${t.method}`);
92
+ }
93
+ // Coverage summary
94
+ if (coverageLines.length > 0) {
95
+ const covered = coverageLines.filter(l => l.executed).length;
96
+ const total = coverageLines.length;
97
+ const pct = total > 0 ? Math.round((covered / total) * 100) : 0;
98
+ lines.push(`\nCobertura: ${covered}/${total} statements (${pct}%)`);
99
+ const uncovered = coverageLines.filter(l => !l.executed).map(l => l.line);
100
+ if (uncovered.length > 0 && uncovered.length <= 50) {
101
+ lines.push(`Linhas não cobertas: ${uncovered.join(", ")}`);
102
+ }
103
+ else if (uncovered.length > 50) {
104
+ lines.push(`Linhas não cobertas: ${uncovered.slice(0, 50).join(", ")} ... (+${uncovered.length - 50})`);
105
+ }
106
+ }
107
+ else {
108
+ lines.push(`\nCobertura: dados de coverage não retornados pelo servidor.`);
109
+ }
110
+ return lines.join("\n");
111
+ }
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCreateAmdp = abapCreateAmdp;
4
+ const create_js_1 = require("./create.js");
5
+ const write_js_1 = require("./write.js");
6
+ const activate_js_1 = require("./activate.js");
7
+ // ---------------------------------------------------------------------------
8
+ // Templates
9
+ // ---------------------------------------------------------------------------
10
+ function buildAmdpClass(input) {
11
+ const { class_name, method_name, method_type, importing = [], exporting_table, cds_entity, } = input;
12
+ const cls = class_name.toLowerCase();
13
+ const meth = method_name.toLowerCase();
14
+ const importingParams = importing
15
+ .map((p) => ` VALUE(${p.name}) TYPE ${p.type}`)
16
+ .join("\n");
17
+ if (method_type === "table_function") {
18
+ // AMDP Table Function — used with CDS table function
19
+ return `CLASS ${cls} DEFINITION PUBLIC FINAL CREATE PUBLIC.
20
+ PUBLIC SECTION.
21
+ INTERFACES if_amdp_marker_hdb.
22
+ CLASS-METHODS ${meth}
23
+ FOR TABLE FUNCTION ${cds_entity || class_name.toUpperCase().replace(/^ZCL_/, "ZTF_")}.
24
+ ENDCLASS.
25
+
26
+ CLASS ${cls} IMPLEMENTATION.
27
+ METHOD ${meth} BY DATABASE FUNCTION FOR HDB
28
+ LANGUAGE SQLSCRIPT
29
+ OPTIONS READ-ONLY
30
+ USING ${cds_entity ? cds_entity.toLowerCase() : "/* tabela base */"}.
31
+
32
+ -- TODO: Implementar SQLScript
33
+ RETURN
34
+ SELECT *
35
+ FROM ${cds_entity ? cds_entity.toLowerCase() : "/* tabela base */"};
36
+
37
+ ENDMETHOD.
38
+ ENDCLASS.`;
39
+ }
40
+ // AMDP Procedure
41
+ const exportingLine = exporting_table
42
+ ? `\n EXPORTING\n VALUE(et_result) TYPE ${exporting_table}`
43
+ : "";
44
+ return `CLASS ${cls} DEFINITION PUBLIC FINAL CREATE PUBLIC.
45
+ PUBLIC SECTION.
46
+ INTERFACES if_amdp_marker_hdb.
47
+ METHODS ${meth}
48
+ ${importingParams ? " IMPORTING\n" + importingParams : ""}${exportingLine}.
49
+ ENDCLASS.
50
+
51
+ CLASS ${cls} IMPLEMENTATION.
52
+ METHOD ${meth} BY DATABASE PROCEDURE FOR HDB
53
+ LANGUAGE SQLSCRIPT
54
+ OPTIONS READ-ONLY
55
+ USING /* tabelas usadas */.
56
+
57
+ -- TODO: Implementar SQLScript
58
+ et_result = SELECT * FROM /* tabela */;
59
+
60
+ ENDMETHOD.
61
+ ENDCLASS.`;
62
+ }
63
+ // ---------------------------------------------------------------------------
64
+ // Main
65
+ // ---------------------------------------------------------------------------
66
+ async function abapCreateAmdp(input) {
67
+ const { class_name, method_name, method_type, description, package: pkg = "$TMP", transport_request } = input;
68
+ const name = class_name.toUpperCase();
69
+ const steps = [];
70
+ try {
71
+ await (0, create_js_1.abapCreate)({
72
+ object_type: "CLAS/OC",
73
+ object_name: name,
74
+ description: description || `AMDP ${method_type} - ${method_name}`,
75
+ package: pkg,
76
+ transport_request,
77
+ });
78
+ steps.push(`✓ Classe ${name} criada`);
79
+ const source = buildAmdpClass(input);
80
+ await (0, write_js_1.abapWrite)({
81
+ object_type: "CLAS/OC",
82
+ object_name: name,
83
+ source,
84
+ });
85
+ steps.push(`✓ Source AMDP gravado (${method_type})`);
86
+ await (0, activate_js_1.abapActivate)({
87
+ object_type: "CLAS/OC",
88
+ object_name: name,
89
+ });
90
+ steps.push(`✓ Classe ${name} ativada`);
91
+ steps.push("");
92
+ if (method_type === "table_function") {
93
+ steps.push("📋 Próximos passos:");
94
+ steps.push(" 1. Criar CDS Table Function referenciando esta classe");
95
+ steps.push(" 2. Implementar SQLScript no método");
96
+ steps.push(" → abap_knowledge(action:'get', topics:['abap/amdp'])");
97
+ }
98
+ else {
99
+ steps.push("📋 Próximos passos:");
100
+ steps.push(" 1. Ajustar tabelas no USING e implementar SQLScript");
101
+ steps.push(" 2. Chamar via CALL METHOD ou READ ENTITIES");
102
+ steps.push(" → abap_knowledge(action:'get', topics:['abap/amdp'])");
103
+ }
104
+ return steps.join("\n");
105
+ }
106
+ catch (err) {
107
+ const message = err instanceof Error ? err.message : String(err);
108
+ steps.push(`✗ ERRO: ${message}`);
109
+ return steps.join("\n");
110
+ }
111
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCreateDcl = abapCreateDcl;
4
+ const create_js_1 = require("./create.js");
5
+ const write_js_1 = require("./write.js");
6
+ const activate_js_1 = require("./activate.js");
7
+ // ---------------------------------------------------------------------------
8
+ // Template builder
9
+ // ---------------------------------------------------------------------------
10
+ function buildDclSource(input) {
11
+ const { dcl_name, cds_entity, auth_type, auth_mappings = [], literal_conditions = [], with_user_aspect } = input;
12
+ const conditions = [];
13
+ if (auth_type === "pfcg" || auth_type === "mapping") {
14
+ for (const mapping of auth_mappings) {
15
+ const actvt = mapping.actvt || "03";
16
+ const cdsFields = mapping.cds_fields.join(", ");
17
+ const authFields = [...mapping.auth_fields, `ACTVT = '${actvt}'`].join(", ");
18
+ conditions.push(`(${cdsFields}) = aspect pfcg_auth(${mapping.auth_object}, ${authFields})`);
19
+ }
20
+ }
21
+ for (const lit of literal_conditions) {
22
+ conditions.push(lit);
23
+ }
24
+ if (with_user_aspect) {
25
+ conditions.push("created_by ?= aspect user");
26
+ }
27
+ // If no conditions, use a simple grant all
28
+ if (conditions.length === 0) {
29
+ conditions.push("1 = 1");
30
+ }
31
+ const whereClause = conditions.length === 1
32
+ ? ` where ${conditions[0]}`
33
+ : ` where ${conditions.join("\n and ")}`;
34
+ return `@EndUserText.label: 'Access Control for ${cds_entity}'
35
+ @MappingRole: true
36
+ define role ${dcl_name} {
37
+ grant select on ${cds_entity}
38
+ ${whereClause};
39
+ }`;
40
+ }
41
+ // ---------------------------------------------------------------------------
42
+ // Main
43
+ // ---------------------------------------------------------------------------
44
+ async function abapCreateDcl(input) {
45
+ const { dcl_name, cds_entity, package: pkg = "$TMP", transport_request } = input;
46
+ const name = dcl_name.toUpperCase();
47
+ const steps = [];
48
+ try {
49
+ // DCL objects are created as DDLS/DF (they are CDS sources)
50
+ await (0, create_js_1.abapCreate)({
51
+ object_type: "DDLS/DF",
52
+ object_name: name,
53
+ description: `Access Control for ${cds_entity}`,
54
+ package: pkg,
55
+ transport_request,
56
+ });
57
+ steps.push(`✓ DCL ${name} criado`);
58
+ const source = buildDclSource(input);
59
+ await (0, write_js_1.abapWrite)({
60
+ object_type: "DDLS/DF",
61
+ object_name: name,
62
+ source,
63
+ });
64
+ steps.push(`✓ Source DCL gravado`);
65
+ await (0, activate_js_1.abapActivate)({
66
+ object_type: "DDLS/DF",
67
+ object_name: name,
68
+ });
69
+ steps.push(`✓ DCL ${name} ativado`);
70
+ steps.push("");
71
+ steps.push(`📋 DCL ${name} protege a entidade ${cds_entity}`);
72
+ steps.push(` Verifique que ${cds_entity} tem @AccessControl.authorizationCheck: #CHECK`);
73
+ steps.push(` Teste autorizações com SU53 após acesso negado`);
74
+ return steps.join("\n");
75
+ }
76
+ catch (err) {
77
+ const message = err instanceof Error ? err.message : String(err);
78
+ steps.push(`✗ ERRO: ${message}`);
79
+ return steps.join("\n");
80
+ }
81
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCreateTransport = abapCreateTransport;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ async function abapCreateTransport(input) {
6
+ const { description, type = "W", target = "" } = input;
7
+ const body = `<?xml version="1.0" encoding="UTF-8"?>
8
+ <tm:root xmlns:tm="http://www.sap.com/cts/adt/tm">
9
+ <tm:request tm:desc="${description}" tm:type="${type}" tm:target="${target}" tm:cts_project=""/>
10
+ </tm:root>`;
11
+ const { data, status, headers } = await (0, adt_client_js_1.adtPostXml)("/cts/transportrequests", body, undefined, "application/vnd.sap.adt.transportorganizer.v1+xml");
12
+ if (status >= 400) {
13
+ const msgMatch = data.match(/<(?:message|shortText)[^>]*>([\s\S]*?)<\/(?:message|shortText)>/i);
14
+ throw new Error(`Erro ao criar transporte (HTTP ${status}): ${msgMatch?.[1] ?? data.slice(0, 500)}`);
15
+ }
16
+ // Extrair número da ordem do response ou Location header
17
+ let trId = "";
18
+ const location = headers["location"] ?? "";
19
+ const locMatch = location.match(/transportrequests\/([A-Z0-9]+)/i);
20
+ if (locMatch) {
21
+ trId = locMatch[1];
22
+ }
23
+ else {
24
+ const idMatch = data.match(/(?:tm:)?(?:number|id)\s*=\s*"([^"]*)"/i);
25
+ trId = idMatch?.[1] ?? "";
26
+ }
27
+ // Fallback: buscar no corpo
28
+ if (!trId) {
29
+ const bodyMatch = data.match(/([A-Z]{3,4}K\d{6})/);
30
+ trId = bodyMatch?.[1] ?? "???";
31
+ }
32
+ const typeLabel = type === "W" ? "Workbench" : type === "K" ? "Customizing" : type;
33
+ return `Ordem de transporte criada com sucesso!\n` +
34
+ `Número: ${trId}\n` +
35
+ `Tipo: ${typeLabel}\n` +
36
+ `Descrição: ${description}` +
37
+ (target ? `\nDestino: ${target}` : "");
38
+ }