abapgit-agent 1.15.3 → 1.16.1
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/abap/CLAUDE.md +61 -12
- package/abap/guidelines/abaplint.md +35 -0
- package/abap/guidelines/common-errors.md +78 -1
- package/abap/guidelines/string-template.md +84 -0
- package/package.json +2 -1
- package/src/commands/drop.js +171 -0
- package/src/commands/dump.js +30 -0
- package/src/commands/health.js +12 -0
- package/src/commands/help.js +32 -1
- package/src/commands/index.js +2 -1
- package/src/commands/inspect.js +23 -0
- package/src/commands/lint.js +26 -0
- package/src/commands/list.js +25 -0
- package/src/commands/preview.js +30 -0
- package/src/commands/pull.js +32 -0
- package/src/commands/run.js +21 -0
- package/src/commands/status.js +13 -0
- package/src/commands/syntax.js +143 -17
- package/src/commands/tree.js +22 -0
- package/src/commands/unit.js +23 -0
- package/src/commands/upgrade.js +28 -0
- package/src/commands/view.js +29 -0
- package/src/commands/where.js +24 -0
- package/src/utils/abap-reference.js +9 -10
package/abap/CLAUDE.md
CHANGED
|
@@ -179,26 +179,27 @@ Anything else (SAP namespace object, or SAP-delivered package)?
|
|
|
179
179
|
|
|
180
180
|
---
|
|
181
181
|
|
|
182
|
-
### 4. Use Syntax Command Before Commit (for CLAS, INTF, PROG, DDLS)
|
|
182
|
+
### 4. Use Syntax Command Before Commit (for CLAS, INTF, PROG, DDLS, FUGR)
|
|
183
183
|
|
|
184
184
|
```
|
|
185
185
|
❌ WRONG: Make changes → Commit → Push → Pull → Find errors → Fix → Repeat
|
|
186
186
|
✅ CORRECT: Make changes → Run syntax → Fix locally → Commit → Push → Pull → Done
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
-
**For CLAS, INTF, PROG, DDLS files**: Run `syntax` command BEFORE commit to catch errors early.
|
|
189
|
+
**For CLAS, INTF, PROG, DDLS, FUGR files**: Run `syntax` command BEFORE commit to catch errors early.
|
|
190
190
|
|
|
191
191
|
```bash
|
|
192
192
|
# Check syntax of local code (no commit/push needed)
|
|
193
193
|
# Use the actual filename from your project (name comes from objects.local.md)
|
|
194
194
|
abapgit-agent syntax --files src/<name>.clas.abap
|
|
195
195
|
abapgit-agent syntax --files src/<name>.ddls.asddls
|
|
196
|
+
abapgit-agent syntax --files src/<name>.fugr.<fm_name>.abap
|
|
196
197
|
|
|
197
198
|
# Check multiple INDEPENDENT files
|
|
198
199
|
abapgit-agent syntax --files src/<name1>.clas.abap,src/<name2>.clas.abap
|
|
199
200
|
```
|
|
200
201
|
|
|
201
|
-
**For other types (
|
|
202
|
+
**For other types (TABL, STRU, DCLS, etc.)**: Skip syntax, proceed to commit/push/pull.
|
|
202
203
|
|
|
203
204
|
**Why use syntax command?**
|
|
204
205
|
- Catches syntax errors BEFORE polluting git history with fix commits
|
|
@@ -424,7 +425,39 @@ After activating a class, stop and tell the user: `"Class is activated. Run with
|
|
|
424
425
|
|
|
425
426
|
---
|
|
426
427
|
|
|
427
|
-
### 10.
|
|
428
|
+
### 10. Never Run `drop` Command Without Explicit Permission
|
|
429
|
+
|
|
430
|
+
**Never call `abapgit-agent drop` unless the user explicitly confirms.** Dropping an object physically deletes it from the ABAP system — this is irreversible without a subsequent pull.
|
|
431
|
+
|
|
432
|
+
**When to SUGGEST `drop --pull`:**
|
|
433
|
+
|
|
434
|
+
When an object is in a broken or inconsistent state that cannot be resolved by re-pulling:
|
|
435
|
+
- `inspect` reports **"INCLUDE report ... not found"** (stale include in ABAP not present in git)
|
|
436
|
+
- Pull repeatedly fails with **"Activation cancelled"** and re-pulling doesn't fix it
|
|
437
|
+
- Object has a corrupt inactive version that permanently blocks activation
|
|
438
|
+
|
|
439
|
+
In these cases, suggest the fix and wait for confirmation:
|
|
440
|
+
|
|
441
|
+
```
|
|
442
|
+
"The object ZCL_FOO has an inconsistent state (stale include in ABAP).
|
|
443
|
+
This can be fixed by dropping and re-pulling it:
|
|
444
|
+
|
|
445
|
+
abapgit-agent drop --files abap/zcl_foo.clas.abap --pull
|
|
446
|
+
|
|
447
|
+
This will delete ZCL_FOO from ABAP and immediately re-activate it from git.
|
|
448
|
+
Confirm when ready."
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**What `drop --pull` does:**
|
|
452
|
+
1. Physically deletes the object from the ABAP system
|
|
453
|
+
2. Immediately re-pulls and re-activates it from git (clean state)
|
|
454
|
+
3. Does NOT touch the git repository file
|
|
455
|
+
|
|
456
|
+
**Constraint:** Only CLAS, INTF, PROG, TABL, TTYP are supported. DTEL (data elements) are rejected — edit the XML and use `pull` instead.
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
### 11. Probe and PoC Objects — Always Z/Y, Never in SAP Packages
|
|
428
461
|
|
|
429
462
|
```
|
|
430
463
|
❌ WRONG: Create a probe/PoC object with the project's SAP namespace prefix
|
|
@@ -450,7 +483,7 @@ Never assume — wait for the user's answer before proceeding.
|
|
|
450
483
|
|
|
451
484
|
---
|
|
452
485
|
|
|
453
|
-
###
|
|
486
|
+
### 12. Troubleshooting ABAP Issues
|
|
454
487
|
|
|
455
488
|
| Symptom | Tool | When |
|
|
456
489
|
|---|---|---|
|
|
@@ -461,7 +494,16 @@ Never assume — wait for the user's answer before proceeding.
|
|
|
461
494
|
|
|
462
495
|
**Critical rules for `debug` sessions:**
|
|
463
496
|
|
|
464
|
-
|
|
497
|
+
```
|
|
498
|
+
❌ WRONG: abapgit-agent debug attach
|
|
499
|
+
❌ WRONG: abapgit-agent debug vars
|
|
500
|
+
❌ WRONG: abapgit-agent debug step --type continue
|
|
501
|
+
✅ CORRECT: abapgit-agent debug attach --json > /tmp/a.json 2>&1 &
|
|
502
|
+
✅ CORRECT: abapgit-agent debug vars --json
|
|
503
|
+
✅ CORRECT: abapgit-agent debug step --type continue --json
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
1. **Always use `--json`** for ALL debug commands (`attach`, `vars`, `stack`, `step`) — human output is not machine-parseable. This is non-negotiable.
|
|
465
507
|
2. **Attach BEFORE trigger** — start `debug attach --json` in background first, wait for `"Listener active"`, THEN fire the trigger (`unit`/`pull`/`run`)
|
|
466
508
|
3. **Never pull to trigger** if a simpler trigger works — use `unit` when a test exists, `run` for a class runner; use `pull` only when the bug is specifically in the pull flow
|
|
467
509
|
4. **Never pass `--session`** to `step/vars/stack` — it bypasses the daemon and causes errors
|
|
@@ -469,7 +511,12 @@ Never assume — wait for the user's answer before proceeding.
|
|
|
469
511
|
|
|
470
512
|
**Finding the right line number for a breakpoint:**
|
|
471
513
|
|
|
472
|
-
|
|
514
|
+
```
|
|
515
|
+
❌ WRONG: abapgit-agent debug set --objects ZCL_FOO:42 ← never guess a line number
|
|
516
|
+
✅ CORRECT: abapgit-agent view --objects ZCL_FOO --full --lines ← get exact line from output
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
Always run `view --full --lines` first — it prints a ready-to-use `debug set` command for every method. Never guess or estimate line numbers.
|
|
473
520
|
|
|
474
521
|
```bash
|
|
475
522
|
abapgit-agent view --objects ZCL_FOO --full --lines
|
|
@@ -509,7 +556,8 @@ abapgit-agent debug set --objects LSUSRU04:50
|
|
|
509
556
|
|
|
510
557
|
Minimal correct sequence:
|
|
511
558
|
```bash
|
|
512
|
-
abapgit-agent
|
|
559
|
+
abapgit-agent view --objects ZCL_FOO --full --lines # 0. get exact line number from output hint
|
|
560
|
+
abapgit-agent debug set --objects ZCL_FOO:42 # 1. set breakpoint (use line from step 0)
|
|
513
561
|
abapgit-agent debug attach --json > /tmp/a.json 2>&1 & # 2. attach (background)
|
|
514
562
|
until grep -q "Listener active" /tmp/a.json 2>/dev/null; do sleep 0.3; done
|
|
515
563
|
abapgit-agent unit --files src/zcl_foo.clas.testclasses.abap > /tmp/t.json 2>&1 & # 3. trigger
|
|
@@ -522,7 +570,7 @@ abapgit-agent debug step --type continue --json # 4. release
|
|
|
522
570
|
|
|
523
571
|
---
|
|
524
572
|
|
|
525
|
-
###
|
|
573
|
+
### 13. abaplint — Static Analysis (Optional, Project-Controlled)
|
|
526
574
|
|
|
527
575
|
abaplint is **optional**. Only run it if `.abaplint.json` exists in the project root.
|
|
528
576
|
Each project defines its own rules — never assume which rules are active.
|
|
@@ -722,18 +770,18 @@ abapgit-agent pull --files src/<name>.clas.abap --sync-xml
|
|
|
722
770
|
|
|
723
771
|
```
|
|
724
772
|
Modified ABAP files?
|
|
725
|
-
├─ CLAS/INTF/PROG/DDLS files?
|
|
773
|
+
├─ CLAS/INTF/PROG/DDLS/FUGR files?
|
|
726
774
|
│ ├─ Independent files (no cross-dependencies)?
|
|
727
775
|
│ │ └─ ✅ Use: syntax → [abaplint] → commit → push → pull --sync-xml
|
|
728
776
|
│ └─ Dependent files (interface + class, class uses class)?
|
|
729
777
|
│ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --sync-xml
|
|
730
|
-
└─ Other types (
|
|
778
|
+
└─ Other types (TABL, STRU, DTEL, TTYP, etc.)?
|
|
731
779
|
├─ XML-only objects (TABL, STRU, DTEL, TTYP, DOMA, MSAG)?
|
|
732
780
|
│ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files abap/ztable.tabl.xml --sync-xml
|
|
733
781
|
├─ DCLS (CDS access control)?
|
|
734
782
|
│ └─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --files abap/zc_view.dcls.xml --sync-xml
|
|
735
783
|
│ ⚠️ Pass the .xml file — pull --files does NOT accept .asdcls extensions
|
|
736
|
-
└─
|
|
784
|
+
└─ Other complex objects?
|
|
737
785
|
└─ ✅ Use: skip syntax → [abaplint] → commit → push → pull --sync-xml → (if errors: inspect)
|
|
738
786
|
|
|
739
787
|
[abaplint] = run abapgit-agent lint only if .abaplint.json exists in repo root
|
|
@@ -761,6 +809,7 @@ Modified ABAP files?
|
|
|
761
809
|
| `ref --topic cds-testing` | CDS Testing (Test Double Framework) |
|
|
762
810
|
| `ref --topic json` | JSON Handling |
|
|
763
811
|
| `ref --topic common-errors` | Common ABAP Errors - Quick Fixes |
|
|
812
|
+
| `ref --topic string-template` | String Templates — syntax, escaping `\{` `\}`, JSON payloads |
|
|
764
813
|
| `ref --topic abapgit` | abapGit XML Metadata — **use for CDS/DDLS XML**, also CLAS, INTF, PROG, DCLS, FUGR |
|
|
765
814
|
| `ref --topic abapgit-xml-only` | abapGit XML Metadata — XML-only objects (TABL, STRU, DTEL, TTYP, DOMA, MSAG) |
|
|
766
815
|
| `ref --topic abapgit-fugr` | abapGit XML Metadata — Function Group (FUGR) details |
|
|
@@ -80,6 +80,41 @@ lv_response = lv_response && '"key":"val"}'.
|
|
|
80
80
|
| `DATA(lv) = 'literal'.` followed by `&&` | ❌ | Infers `C LENGTH N`, truncates |
|
|
81
81
|
| `DATA(lv) = 'literal'.` used only in `\|{ lv }\|` | ⚠️ | Technically works but misleading — prefer explicit type |
|
|
82
82
|
| `DATA(lv) = 'X'.` used as abap_bool flag | ✅ | `C LENGTH 1` is correct for flags |
|
|
83
|
+
| `DATA(lv) = COND string( WHEN ... THEN str+off ELSE str ).` | ❌ | Offset notation on `string` inside COND raises `CX_SY_RANGE_OUT_OF_BOUNDS` at runtime |
|
|
84
|
+
|
|
85
|
+
### The offset-notation-in-COND trap
|
|
86
|
+
|
|
87
|
+
Offset/length notation (`str+off` or `str(len)`) on a `string`-typed variable **cannot appear
|
|
88
|
+
as a result expression inside `COND`** — even when the `COND` is explicitly typed as `string`.
|
|
89
|
+
ABAP evaluates both branch types at generation time and the offset expression on a dynamic
|
|
90
|
+
string is invalid, causing a `CX_SY_RANGE_OUT_OF_BOUNDS` crash at runtime.
|
|
91
|
+
|
|
92
|
+
```abap
|
|
93
|
+
DATA lv_file TYPE string VALUE 'src/foo.clas.abap'.
|
|
94
|
+
DATA(lv_pos) = find( val = lv_file sub = '/' occ = -1 ).
|
|
95
|
+
|
|
96
|
+
* WRONG — CX_SY_RANGE_OUT_OF_BOUNDS at runtime
|
|
97
|
+
DATA(lv_path) = COND string(
|
|
98
|
+
WHEN lv_pos > 0 THEN '/' && lv_file(lv_pos + 1) " ← offset on string = crash
|
|
99
|
+
ELSE '/' ).
|
|
100
|
+
|
|
101
|
+
* CORRECT — use substring() which returns string and is safe in COND
|
|
102
|
+
DATA(lv_path) = COND string(
|
|
103
|
+
WHEN lv_pos > 0 THEN '/' && substring( val = lv_file len = lv_pos + 1 )
|
|
104
|
+
ELSE '/' ).
|
|
105
|
+
|
|
106
|
+
* ALSO CORRECT — keep IF/ELSE with DATA x TYPE string
|
|
107
|
+
DATA lv_path TYPE string.
|
|
108
|
+
IF lv_pos > 0.
|
|
109
|
+
DATA(lv_len) = lv_pos + 1.
|
|
110
|
+
lv_path = '/' && lv_file(lv_len).
|
|
111
|
+
ELSE.
|
|
112
|
+
lv_path = '/'.
|
|
113
|
+
ENDIF.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Rule**: whenever the prefer_inline quickfix would produce `COND ... THEN str+off` or
|
|
117
|
+
`COND ... THEN str(len)`, use `substring()` instead, or keep the `IF/ELSE` form.
|
|
83
118
|
|
|
84
119
|
### Rule of thumb
|
|
85
120
|
|
|
@@ -8,7 +8,7 @@ grand_parent: ABAP Development
|
|
|
8
8
|
|
|
9
9
|
# Common ABAP Errors - Quick Fixes
|
|
10
10
|
|
|
11
|
-
**Searchable keywords**: error, syntax error, field mismatch, fixed point, comma, host variable, @ prefix, type incompatible
|
|
11
|
+
**Searchable keywords**: error, syntax error, field mismatch, fixed point, comma, host variable, @ prefix, type incompatible, string template, expression limiter, literal brace, http 500, xml metadata mismatch, interface description, descript, recompilation, offset notation, cond trap, cx_sy_range_out_of_bounds
|
|
12
12
|
|
|
13
13
|
## Fixed Point Arithmetic Error
|
|
14
14
|
|
|
@@ -119,6 +119,83 @@ or declare the variable explicitly with `TYPE string`.
|
|
|
119
119
|
|
|
120
120
|
---
|
|
121
121
|
|
|
122
|
+
## Offset Notation on String in COND — CX_SY_RANGE_OUT_OF_BOUNDS
|
|
123
|
+
|
|
124
|
+
**Symptom**: Pull succeeds but ABAP crashes at runtime with `CX_SY_RANGE_OUT_OF_BOUNDS`
|
|
125
|
+
inside a method using `COND`.
|
|
126
|
+
|
|
127
|
+
**Root cause**: Offset/length notation on a `string` variable (`str+off`, `str(len)`)
|
|
128
|
+
**cannot be used as a result expression inside `COND`** — even with an explicit `COND string(...)` type.
|
|
129
|
+
The abaplint `prefer_inline` quickfix can introduce this automatically when converting
|
|
130
|
+
an `IF/ELSE` block that assigns from a string-variable offset.
|
|
131
|
+
|
|
132
|
+
```abap
|
|
133
|
+
* WRONG — crashes at runtime with CX_SY_RANGE_OUT_OF_BOUNDS
|
|
134
|
+
DATA(lv_path) = COND string(
|
|
135
|
+
WHEN lv_pos > 0 THEN '/' && lv_file(lv_pos + 1) " ← string offset in COND
|
|
136
|
+
ELSE '/' ).
|
|
137
|
+
|
|
138
|
+
* CORRECT — use substring() instead
|
|
139
|
+
DATA(lv_path) = COND string(
|
|
140
|
+
WHEN lv_pos > 0 THEN '/' && substring( val = lv_file len = lv_pos + 1 )
|
|
141
|
+
ELSE '/' ).
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Fix**: Replace `str+off` / `str(len)` inside `COND` results with `substring( val = str off = off )` or
|
|
145
|
+
`substring( val = str len = len )`, or revert to the `IF/ELSE` form with `DATA x TYPE string`.
|
|
146
|
+
|
|
147
|
+
→ See `abaplint.md` for the full safe/unsafe table for `prefer_inline`.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## XML Metadata Mismatch — HTTP 500 on Pull
|
|
152
|
+
|
|
153
|
+
**Symptom**: `pull` returns HTTP 500 with no short dump (ST22) for the request user.
|
|
154
|
+
Other objects pull fine; only a specific interface or class fails.
|
|
155
|
+
|
|
156
|
+
**Root cause**: The `.intf.xml` or `.clas.xml` file in git has a metadata field (e.g. `DESCRIPT`,
|
|
157
|
+
`UNICODE`, `STATE`) that differs from the active ABAP system value. abapGit writes the git
|
|
158
|
+
value back to VSEOINTERF/VSEOCLASS, which forces the SAP class framework to recompile all
|
|
159
|
+
implementing/inheriting objects. If any implementing class has a syntax or generation
|
|
160
|
+
issue with the current active source, the recompilation crashes outside the normal exception
|
|
161
|
+
handler — producing HTTP 500 with no dump.
|
|
162
|
+
|
|
163
|
+
**Diagnosis**:
|
|
164
|
+
1. No dump in ST22 for your user → not a user-code crash
|
|
165
|
+
2. Only one specific INTF/CLAS fails → metadata delta in its XML triggers recompilation
|
|
166
|
+
3. Check what differs: `abapgit-agent view --objects ZIF_MY_INTF --type INTF` and compare
|
|
167
|
+
`DESCRIPT` / other fields with the XML file in git
|
|
168
|
+
|
|
169
|
+
**Fix**: Sync the XML to match the ABAP system value. The easiest way:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Pull without --sync-xml first to let abapGit rewrite the XML from the system
|
|
173
|
+
abapgit-agent pull --files abap/zif_my_intf.intf.abap --sync-xml
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Or manually correct the field in the XML file to match the system value, then push and pull.
|
|
177
|
+
|
|
178
|
+
**Prevention**: Always use `--sync-xml` after pulling new objects so the XML stays in sync
|
|
179
|
+
with what the ABAP serializer produces.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
**Error**: `Expression limiter '{' in string template not followed by space`
|
|
184
|
+
|
|
185
|
+
Literal `{` and `}` (e.g. in JSON payloads) must be escaped as `\{` and `\}` inside `|...|`.
|
|
186
|
+
|
|
187
|
+
```abap
|
|
188
|
+
" WRONG — unescaped { in JSON triggers parse error
|
|
189
|
+
rv_result = |{"success":"X","name":"{ lv_name }"}|.
|
|
190
|
+
|
|
191
|
+
" CORRECT
|
|
192
|
+
rv_result = |\{"success":"X","name":"{ lv_name }"\}|.
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
→ Full escaping rules and examples: `abapgit-agent ref --topic string-template`
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
122
199
|
## See Also
|
|
123
200
|
- **ABAP SQL** (sql.md) - for SQL syntax rules
|
|
124
201
|
- **CDS Views** (cds.md) - for CDS selection patterns
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: String Templates
|
|
4
|
+
nav_order: 25
|
|
5
|
+
parent: ABAP Coding Guidelines
|
|
6
|
+
grand_parent: ABAP Development
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# String Templates
|
|
10
|
+
|
|
11
|
+
**Searchable keywords**: string template, pipe, `|...|`, literal brace, escape, expression limiter, JSON, `\{`, `\}`
|
|
12
|
+
|
|
13
|
+
## Syntax
|
|
14
|
+
|
|
15
|
+
String templates are delimited by `|...|`. Embedded expressions use `{ expr }`:
|
|
16
|
+
|
|
17
|
+
```abap
|
|
18
|
+
DATA(s) = |Hello { lv_name }!|.
|
|
19
|
+
DATA(s) = |User: { cl_abap_context_info=>get_user_technical_name( ) }|.
|
|
20
|
+
DATA(s) = |Length: { strlen( lv_text ) }|.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Escaping Special Characters
|
|
24
|
+
|
|
25
|
+
Inside `|...|`, four characters have special meaning and must be escaped with `\`:
|
|
26
|
+
|
|
27
|
+
| Character | Escape as | Produces |
|
|
28
|
+
|-----------|-----------|---------|
|
|
29
|
+
| `{` | `\{` | literal `{` |
|
|
30
|
+
| `}` | `\}` | literal `}` |
|
|
31
|
+
| `\` | `\\` | literal `\` |
|
|
32
|
+
| `\|` | `\|` | literal `\|` |
|
|
33
|
+
|
|
34
|
+
```abap
|
|
35
|
+
" Produces: \ | { }
|
|
36
|
+
DATA(s) = |\\ \| \{ \}|.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## JSON Payloads — The Most Common Mistake
|
|
40
|
+
|
|
41
|
+
JSON objects start with `{` — which ABAP treats as an expression delimiter.
|
|
42
|
+
**Always escape outer JSON braces as `\{` and `\}`.**
|
|
43
|
+
|
|
44
|
+
```abap
|
|
45
|
+
" WRONG — "Expression limiter '{' in string template not followed by space"
|
|
46
|
+
rv_result = |{"success":"X","name":"{ lv_name }"}|.
|
|
47
|
+
" ^ unescaped { triggers parse error
|
|
48
|
+
|
|
49
|
+
" CORRECT — outer JSON braces escaped, expression { } left as-is
|
|
50
|
+
rv_result = |\{"success":"X","name":"{ lv_name }"\}|.
|
|
51
|
+
|
|
52
|
+
" CORRECT — error with method call
|
|
53
|
+
rv_result = |\{"success":"","error":"{ lx_error->get_text( ) }"\}|.
|
|
54
|
+
|
|
55
|
+
" CORRECT — multiple embedded fields
|
|
56
|
+
rv_result = |\{"success":"X","object":"{ lv_obj_name }","type":"{ lv_obj_type }"\}|.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Control Characters
|
|
60
|
+
|
|
61
|
+
```abap
|
|
62
|
+
DATA(s) = |line1\nline2|. " newline
|
|
63
|
+
DATA(s) = |col1\tcol2|. " tab
|
|
64
|
+
DATA(s) = |line1\r\nline2|. " CR+LF
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Chaining with `&&`
|
|
68
|
+
|
|
69
|
+
```abap
|
|
70
|
+
DATA(s) = |{ lv_a }| && ` separator ` && |{ lv_b }|.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Performance Note
|
|
74
|
+
|
|
75
|
+
A string template containing only literal text (no `{ }` expressions) is evaluated at
|
|
76
|
+
runtime. Prefer backquote literals for pure text:
|
|
77
|
+
|
|
78
|
+
```abap
|
|
79
|
+
" Prefer this for literal-only strings
|
|
80
|
+
DATA(s) = `Hello World`.
|
|
81
|
+
|
|
82
|
+
" Not this (runtime overhead with no benefit)
|
|
83
|
+
DATA(s) = |Hello World|.
|
|
84
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abapgit-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.1",
|
|
4
4
|
"description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
|
|
5
5
|
"files": [
|
|
6
6
|
"bin/",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"test:cmd:preview": "node tests/run-all.js --cmd --command=preview",
|
|
37
37
|
"test:cmd:tree": "node tests/run-all.js --cmd --command=tree",
|
|
38
38
|
"test:cmd:dump": "node tests/run-all.js --cmd --command=dump",
|
|
39
|
+
"test:drop": "node tests/run-all.js --drop",
|
|
39
40
|
"test:cmd:debug": "node tests/run-all.js --cmd --command=debug",
|
|
40
41
|
"test:debug": "node tests/run-all.js --debug",
|
|
41
42
|
"test:debug:scenarios": "bash tests/integration/debug-scenarios.sh",
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop command - Physically delete a single ABAP object from the ABAP system
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { printHttpError } = require('../utils/format-error');
|
|
6
|
+
|
|
7
|
+
// Map file extension (second-to-last part) to ABAP object type label
|
|
8
|
+
const EXT_TO_TYPE = {
|
|
9
|
+
clas: 'CLAS', intf: 'INTF', prog: 'PROG', fugr: 'FUGR',
|
|
10
|
+
tabl: 'TABL', dtel: 'DTEL', ttyp: 'TTYP', doma: 'DOMA',
|
|
11
|
+
ddls: 'DDLS', dcls: 'DCLS', msag: 'MSAG', stru: 'STRU'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Derive a display label for the object from the file path.
|
|
16
|
+
* e.g. "abap/zcl_foo.clas.abap" → { name: "ZCL_FOO", type: "CLAS" }
|
|
17
|
+
*/
|
|
18
|
+
function objectFromFile(filePath) {
|
|
19
|
+
const base = filePath.split('/').pop();
|
|
20
|
+
const parts = base.split('.');
|
|
21
|
+
if (parts.length < 3) return null;
|
|
22
|
+
const name = parts[0].toUpperCase();
|
|
23
|
+
const typeExt = parts[1].toLowerCase();
|
|
24
|
+
const type = EXT_TO_TYPE[typeExt] || typeExt.toUpperCase();
|
|
25
|
+
return { name, type };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
name: 'drop',
|
|
30
|
+
description: 'Physically delete a single ABAP object from the ABAP system',
|
|
31
|
+
requiresAbapConfig: true,
|
|
32
|
+
requiresVersionCheck: true,
|
|
33
|
+
|
|
34
|
+
async execute(args, context) {
|
|
35
|
+
const { loadConfig, AbapHttp, gitUtils, getTransport, getConflictSettings } = context;
|
|
36
|
+
|
|
37
|
+
// Show help if requested
|
|
38
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
39
|
+
console.log(`
|
|
40
|
+
Usage:
|
|
41
|
+
abapgit-agent drop --files <file>
|
|
42
|
+
abapgit-agent drop --files <file> --transport <TRANSPORT>
|
|
43
|
+
abapgit-agent drop --files <file> --pull
|
|
44
|
+
|
|
45
|
+
Description:
|
|
46
|
+
Physically deletes a single ABAP object from the ABAP system using abapGit's
|
|
47
|
+
own object serializer. The object type and name are derived from the file path.
|
|
48
|
+
|
|
49
|
+
Use --pull to immediately re-activate the object from git after deletion
|
|
50
|
+
(useful to force a clean re-installation).
|
|
51
|
+
|
|
52
|
+
Parameters:
|
|
53
|
+
--files <file> Path to the ABAP source or XML file (required).
|
|
54
|
+
Accepted extensions: .abap, .asddls, .tabl.xml, etc.
|
|
55
|
+
The file must exist on disk.
|
|
56
|
+
--transport <TRANSPORT> Transport request (e.g. DEVK900001). Optional.
|
|
57
|
+
--pull Re-activate the object via pull after deletion.
|
|
58
|
+
--conflict-mode <mode> Conflict mode for --pull: abort (default) or ignore.
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
abapgit-agent drop --files abap/zcl_foo.clas.abap
|
|
62
|
+
abapgit-agent drop --files abap/zcl_foo.clas.abap --pull
|
|
63
|
+
abapgit-agent drop --files abap/zmy_table.tabl.xml --transport DEVK900001
|
|
64
|
+
`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const filesArgIndex = args.indexOf('--files');
|
|
69
|
+
const transportArgIndex = args.indexOf('--transport');
|
|
70
|
+
const conflictModeArgIndex = args.indexOf('--conflict-mode');
|
|
71
|
+
const doPull = args.includes('--pull');
|
|
72
|
+
|
|
73
|
+
if (filesArgIndex === -1 || filesArgIndex + 1 >= args.length) {
|
|
74
|
+
console.error('❌ Error: --files is required');
|
|
75
|
+
console.error(' Usage: abapgit-agent drop --files <file>');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const filePath = args[filesArgIndex + 1].trim();
|
|
80
|
+
|
|
81
|
+
// Validate file exists on disk
|
|
82
|
+
const fs = require('fs');
|
|
83
|
+
if (!fs.existsSync(filePath)) {
|
|
84
|
+
console.error(`❌ Error: file not found: ${filePath}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Validate file extension (same rules as pull --files)
|
|
89
|
+
const base = filePath.split('/').pop();
|
|
90
|
+
const parts = base.split('.');
|
|
91
|
+
const lastExt = parts[parts.length - 1].toLowerCase();
|
|
92
|
+
const ABAP_SOURCE_EXTS = new Set(['abap', 'asddls']);
|
|
93
|
+
const isXmlOnlyObject = parts.length === 3 && parts[0].length > 0 && lastExt === 'xml';
|
|
94
|
+
if (!ABAP_SOURCE_EXTS.has(lastExt) && !isXmlOnlyObject) {
|
|
95
|
+
console.error('❌ Error: --files must be an ABAP source file (.abap, .asddls) or an XML-only object file (name.type.xml).');
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const obj = objectFromFile(filePath);
|
|
100
|
+
|
|
101
|
+
if (obj && obj.type === 'DTEL') {
|
|
102
|
+
console.error(`❌ drop does not support DTEL objects.`);
|
|
103
|
+
console.error(` Data elements cannot be re-activated after deletion due to SAP CBDA`);
|
|
104
|
+
console.error(` activation engine limitations. Edit the XML file and run pull instead.`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const transportRequest = transportArgIndex !== -1 ? args[transportArgIndex + 1] : (getTransport ? getTransport() : null);
|
|
109
|
+
const conflictMode = conflictModeArgIndex !== -1 ? args[conflictModeArgIndex + 1] : (getConflictSettings ? getConflictSettings().mode : 'abort');
|
|
110
|
+
|
|
111
|
+
console.log(`\n🗑️ Dropping ${obj ? obj.type + ' ' + obj.name : filePath} from ABAP system...`);
|
|
112
|
+
if (transportRequest) {
|
|
113
|
+
console.log(` Transport: ${transportRequest}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const config = loadConfig();
|
|
117
|
+
const http = new AbapHttp(config);
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const csrfToken = await http.fetchCsrfToken();
|
|
121
|
+
|
|
122
|
+
const data = { file: filePath };
|
|
123
|
+
if (transportRequest) {
|
|
124
|
+
data.transport_request = transportRequest;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result = await http.post('/sap/bc/z_abapgit_agent/drop', data, { csrfToken });
|
|
128
|
+
|
|
129
|
+
const success = result.SUCCESS || result.success;
|
|
130
|
+
const objectName = result.OBJECT || result.object;
|
|
131
|
+
const objectType = result.TYPE || result.type;
|
|
132
|
+
const message = result.MESSAGE || result.message;
|
|
133
|
+
const error = result.ERROR || result.error;
|
|
134
|
+
|
|
135
|
+
if (success === 'X' || success === true) {
|
|
136
|
+
console.log(`✅ Object deleted successfully.`);
|
|
137
|
+
if (objectName && objectType) {
|
|
138
|
+
console.log(` Object: ${objectName} (${objectType})`);
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
console.error(`❌ Failed to delete object`);
|
|
142
|
+
console.error(` Error: ${error || message || 'Unknown error'}`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
printHttpError(error, {});
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// --pull: re-activate the object from git
|
|
151
|
+
if (doPull) {
|
|
152
|
+
console.log(`\n↩️ Re-pulling from git...`);
|
|
153
|
+
const pullCommand = require('./pull');
|
|
154
|
+
const gitUrl = gitUtils.getRemoteUrl();
|
|
155
|
+
const branch = gitUtils.getBranch();
|
|
156
|
+
if (!gitUrl) {
|
|
157
|
+
console.error('❌ Cannot re-pull: no git remote configured.');
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
await pullCommand.pull(
|
|
162
|
+
gitUrl, branch, [filePath], transportRequest || null,
|
|
163
|
+
loadConfig, AbapHttp, false, undefined, conflictMode, false, false
|
|
164
|
+
);
|
|
165
|
+
} catch (pullError) {
|
|
166
|
+
// pull() already printed the error
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
package/src/commands/dump.js
CHANGED
|
@@ -196,6 +196,36 @@ module.exports = {
|
|
|
196
196
|
async execute(args, context) {
|
|
197
197
|
const { loadConfig, AbapHttp } = context;
|
|
198
198
|
|
|
199
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
200
|
+
console.log(`
|
|
201
|
+
Usage:
|
|
202
|
+
abapgit-agent dump [--user <user>] [--date <date>] [--time <HH:MM..HH:MM>]
|
|
203
|
+
[--program <prog>] [--error <error>] [--limit <n>] [--detail <n>] [--json]
|
|
204
|
+
|
|
205
|
+
Description:
|
|
206
|
+
Query short dumps (ST22) from the ABAP system.
|
|
207
|
+
|
|
208
|
+
Parameters:
|
|
209
|
+
--user <user> Filter by user name.
|
|
210
|
+
--date <date> Filter by date: TODAY, YESTERDAY, or YYYYMMDD or YYYYMMDD..YYYYMMDD.
|
|
211
|
+
--time <HH:MM..HH:MM> Filter by time range (e.g. 08:00..17:00).
|
|
212
|
+
--program <prog> Filter by program name.
|
|
213
|
+
--error <error> Filter by error type (e.g. RABAX_STATE).
|
|
214
|
+
--timezone <tz> Timezone for date/time filter (default: system timezone).
|
|
215
|
+
--limit <n> Maximum number of results (default: 10).
|
|
216
|
+
--detail <n> Show full detail of the Nth result (1-based).
|
|
217
|
+
--json Output as JSON.
|
|
218
|
+
|
|
219
|
+
Examples:
|
|
220
|
+
abapgit-agent dump
|
|
221
|
+
abapgit-agent dump --date TODAY
|
|
222
|
+
abapgit-agent dump --user DEVELOPER
|
|
223
|
+
abapgit-agent dump --date TODAY --limit 20
|
|
224
|
+
abapgit-agent dump --user DEVELOPER --detail 1
|
|
225
|
+
`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
199
229
|
const idx = (flag) => args.indexOf(flag);
|
|
200
230
|
const val = (flag) => {
|
|
201
231
|
const i = idx(flag);
|
package/src/commands/health.js
CHANGED
|
@@ -11,6 +11,18 @@ module.exports = {
|
|
|
11
11
|
async execute(args, context) {
|
|
12
12
|
const { config, AbapHttp } = context;
|
|
13
13
|
|
|
14
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
15
|
+
console.log(`
|
|
16
|
+
Usage:
|
|
17
|
+
abapgit-agent health
|
|
18
|
+
|
|
19
|
+
Description:
|
|
20
|
+
Check if the ABAP REST API (z_abapgit_agent) is reachable and responding.
|
|
21
|
+
Useful for verifying connectivity and configuration.
|
|
22
|
+
`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
try {
|
|
15
27
|
const http = new AbapHttp(config);
|
|
16
28
|
const result = await http.get('/sap/bc/z_abapgit_agent/health');
|