abapgit-agent 1.7.2 → 1.8.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 +7 -7
- package/abap/CLAUDE.md +145 -26
- package/abap/guidelines/00_index.md +8 -0
- package/abap/guidelines/01_sql.md +8 -0
- package/abap/guidelines/02_exceptions.md +8 -0
- package/abap/guidelines/03_testing.md +8 -0
- package/abap/guidelines/04_cds.md +8 -0
- package/abap/guidelines/05_classes.md +8 -0
- package/abap/guidelines/06_objects.md +8 -0
- package/abap/guidelines/07_json.md +8 -0
- package/abap/guidelines/08_abapgit.md +8 -0
- package/abap/guidelines/09_unit_testable_code.md +8 -0
- package/bin/abapgit-agent +61 -2852
- package/package.json +21 -5
- package/src/agent.js +205 -16
- package/src/commands/create.js +102 -0
- package/src/commands/delete.js +72 -0
- package/src/commands/health.js +24 -0
- package/src/commands/help.js +111 -0
- package/src/commands/import.js +99 -0
- package/src/commands/init.js +321 -0
- package/src/commands/inspect.js +184 -0
- package/src/commands/list.js +143 -0
- package/src/commands/preview.js +277 -0
- package/src/commands/pull.js +278 -0
- package/src/commands/ref.js +96 -0
- package/src/commands/status.js +52 -0
- package/src/commands/syntax.js +290 -0
- package/src/commands/tree.js +209 -0
- package/src/commands/unit.js +133 -0
- package/src/commands/view.js +215 -0
- package/src/commands/where.js +138 -0
- package/src/config.js +11 -1
- package/src/utils/abap-http.js +347 -0
- package/src/utils/git-utils.js +58 -0
- package/src/utils/validators.js +72 -0
- package/src/utils/version-check.js +80 -0
- package/src/abap-client.js +0 -526
- /package/src/{ref-search.js → utils/abap-reference.js} +0 -0
package/src/abap-client.js
DELETED
|
@@ -1,526 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ABAP Client - Connects to SAP ABAP system via REST/HTTP
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const https = require('https');
|
|
6
|
-
const http = require('http');
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const { getAbapConfig } = require('./config');
|
|
10
|
-
const logger = require('./logger');
|
|
11
|
-
|
|
12
|
-
class ABAPClient {
|
|
13
|
-
constructor() {
|
|
14
|
-
this.config = null;
|
|
15
|
-
this.cookieFile = path.join(__dirname, '..', '.abapgit_agent_cookies.txt');
|
|
16
|
-
this.csrfToken = null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Get ABAP configuration
|
|
21
|
-
*/
|
|
22
|
-
getConfig() {
|
|
23
|
-
if (!this.config) {
|
|
24
|
-
const cfg = getAbapConfig();
|
|
25
|
-
this.config = {
|
|
26
|
-
baseUrl: `https://${cfg.host}:${cfg.sapport || 44300}/sap/bc/z_abapgit_agent`,
|
|
27
|
-
username: cfg.user,
|
|
28
|
-
password: cfg.password,
|
|
29
|
-
client: cfg.client,
|
|
30
|
-
language: cfg.language || 'EN',
|
|
31
|
-
gitUsername: cfg.gitUsername,
|
|
32
|
-
gitPassword: cfg.gitPassword
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
return this.config;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Read cookies from Netscape format cookie file
|
|
40
|
-
*/
|
|
41
|
-
readNetscapeCookies() {
|
|
42
|
-
if (!fs.existsSync(this.cookieFile)) return '';
|
|
43
|
-
|
|
44
|
-
const content = fs.readFileSync(this.cookieFile, 'utf8');
|
|
45
|
-
const lines = content.split('\n');
|
|
46
|
-
const cookies = [];
|
|
47
|
-
|
|
48
|
-
for (const line of lines) {
|
|
49
|
-
const trimmed = line.trim();
|
|
50
|
-
// Skip empty lines and only the header comments (starting with #)
|
|
51
|
-
// but NOT HttpOnly cookies which start with #HttpOnly_
|
|
52
|
-
if (!trimmed || (trimmed.startsWith('#') && !trimmed.startsWith('#HttpOnly'))) continue;
|
|
53
|
-
|
|
54
|
-
const parts = trimmed.split('\t');
|
|
55
|
-
if (parts.length >= 7) {
|
|
56
|
-
// Format: domain, flag, path, secure, expiration, name, value
|
|
57
|
-
cookies.push(`${parts[5]}=${parts[6]}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return cookies.join('; ');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Make HTTP request
|
|
66
|
-
*/
|
|
67
|
-
async request(method, path, data = null, options = {}) {
|
|
68
|
-
const cfg = this.getConfig();
|
|
69
|
-
|
|
70
|
-
return new Promise((resolve, reject) => {
|
|
71
|
-
const url = new URL(`${cfg.baseUrl}${path}`);
|
|
72
|
-
|
|
73
|
-
const headers = {
|
|
74
|
-
'Content-Type': 'application/json',
|
|
75
|
-
'sap-client': cfg.client,
|
|
76
|
-
'sap-language': cfg.language
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// Add authorization
|
|
80
|
-
if (cfg.username) {
|
|
81
|
-
headers['Authorization'] = `Basic ${Buffer.from(`${cfg.username}:${cfg.password}`).toString('base64')}`;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Add CSRF token for POST
|
|
85
|
-
if (method === 'POST' && options.csrfToken) {
|
|
86
|
-
headers['X-CSRF-Token'] = options.csrfToken;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Add cookies if available (handle Netscape format)
|
|
90
|
-
const cookieHeader = this.readNetscapeCookies();
|
|
91
|
-
if (cookieHeader) {
|
|
92
|
-
headers['Cookie'] = cookieHeader;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const reqOptions = {
|
|
96
|
-
hostname: url.hostname,
|
|
97
|
-
port: url.port,
|
|
98
|
-
path: url.pathname,
|
|
99
|
-
method,
|
|
100
|
-
headers,
|
|
101
|
-
agent: new https.Agent({ rejectUnauthorized: false })
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const req = (url.protocol === 'https:' ? https : http).request(reqOptions, (res) => {
|
|
105
|
-
// Update cookies
|
|
106
|
-
const setCookie = res.headers['set-cookie'];
|
|
107
|
-
if (setCookie) {
|
|
108
|
-
const cookies = Array.isArray(setCookie)
|
|
109
|
-
? setCookie.map(c => c.split(';')[0]).join('; ')
|
|
110
|
-
: setCookie.split(';')[0];
|
|
111
|
-
fs.writeFileSync(this.cookieFile, cookies);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Get CSRF token from response headers (for GET /pull with fetch)
|
|
115
|
-
if (res.headers['x-csrf-token'] && !this.csrfToken) {
|
|
116
|
-
this.csrfToken = res.headers['x-csrf-token'];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
let body = '';
|
|
120
|
-
res.on('data', chunk => body += chunk);
|
|
121
|
-
res.on('end', () => {
|
|
122
|
-
try {
|
|
123
|
-
if (res.statusCode >= 400) {
|
|
124
|
-
logger.error(`REST request failed`, { status: res.statusCode, body });
|
|
125
|
-
reject(new Error(`REST request failed: ${res.statusCode}`));
|
|
126
|
-
} else if (body) {
|
|
127
|
-
resolve(JSON.parse(body));
|
|
128
|
-
} else {
|
|
129
|
-
resolve({});
|
|
130
|
-
}
|
|
131
|
-
} catch (e) {
|
|
132
|
-
resolve(body);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
req.on('error', reject);
|
|
138
|
-
|
|
139
|
-
if (data) {
|
|
140
|
-
req.write(JSON.stringify(data));
|
|
141
|
-
}
|
|
142
|
-
req.end();
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Fetch CSRF token using GET /pull with X-CSRF-Token: fetch
|
|
148
|
-
*/
|
|
149
|
-
async fetchCsrfToken() {
|
|
150
|
-
const cfg = this.getConfig();
|
|
151
|
-
|
|
152
|
-
return new Promise((resolve, reject) => {
|
|
153
|
-
const url = new URL(`${cfg.baseUrl}/pull`);
|
|
154
|
-
|
|
155
|
-
// Clear stale cookies before fetching new token
|
|
156
|
-
if (fs.existsSync(this.cookieFile)) {
|
|
157
|
-
fs.unlinkSync(this.cookieFile);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Read cookies for sending (handle Netscape format)
|
|
161
|
-
const cookieHeader = this.readNetscapeCookies();
|
|
162
|
-
|
|
163
|
-
const options = {
|
|
164
|
-
hostname: url.hostname,
|
|
165
|
-
port: url.port,
|
|
166
|
-
path: url.pathname,
|
|
167
|
-
method: 'GET',
|
|
168
|
-
headers: {
|
|
169
|
-
'Authorization': `Basic ${Buffer.from(`${cfg.username}:${cfg.password}`).toString('base64')}`,
|
|
170
|
-
'sap-client': cfg.client,
|
|
171
|
-
'sap-language': cfg.language,
|
|
172
|
-
'X-CSRF-Token': 'fetch',
|
|
173
|
-
'Content-Type': 'application/json',
|
|
174
|
-
...(cookieHeader && { 'Cookie': cookieHeader })
|
|
175
|
-
},
|
|
176
|
-
agent: new https.Agent({ rejectUnauthorized: false })
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const req = https.request(options, (res) => {
|
|
180
|
-
const csrfToken = res.headers['x-csrf-token'];
|
|
181
|
-
|
|
182
|
-
// Save new cookies from response - the CSRF token is tied to this new session!
|
|
183
|
-
const setCookie = res.headers['set-cookie'];
|
|
184
|
-
if (setCookie) {
|
|
185
|
-
const cookies = Array.isArray(setCookie)
|
|
186
|
-
? setCookie.map(c => c.split(';')[0]).join('; ')
|
|
187
|
-
: setCookie.split(';')[0];
|
|
188
|
-
fs.writeFileSync(this.cookieFile, cookies);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
let body = '';
|
|
192
|
-
res.on('data', chunk => body += chunk);
|
|
193
|
-
res.on('end', () => {
|
|
194
|
-
// Store token in instance for use by POST
|
|
195
|
-
this.csrfToken = csrfToken;
|
|
196
|
-
resolve({ token: csrfToken });
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
req.on('error', reject);
|
|
201
|
-
req.end();
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Pull repository and activate
|
|
207
|
-
* Uses command API if useCommandApi config is enabled, otherwise uses legacy /pull endpoint
|
|
208
|
-
* @param {string} repoUrl - Repository URL
|
|
209
|
-
* @param {string} branch - Branch name (default: 'main')
|
|
210
|
-
* @param {string} gitUsername - Git username (optional)
|
|
211
|
-
* @param {string} gitPassword - Git password/token (optional)
|
|
212
|
-
* @param {Array} files - Array of file paths to pull (optional)
|
|
213
|
-
* @param {string} transportRequest - Transport request number (optional)
|
|
214
|
-
* @returns {object} Pull result
|
|
215
|
-
*/
|
|
216
|
-
async pull(repoUrl, branch = 'main', gitUsername = null, gitPassword = null, files = null, transportRequest = null) {
|
|
217
|
-
const cfg = this.getConfig();
|
|
218
|
-
|
|
219
|
-
// Fetch CSRF token first (using GET /pull with X-CSRF-Token: fetch)
|
|
220
|
-
await this.fetchCsrfToken();
|
|
221
|
-
|
|
222
|
-
const data = {
|
|
223
|
-
url: repoUrl,
|
|
224
|
-
branch: branch
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
// Add files if specified
|
|
228
|
-
if (files && files.length > 0) {
|
|
229
|
-
data.files = files;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Add transport request if specified
|
|
233
|
-
if (transportRequest) {
|
|
234
|
-
data.transport_request = transportRequest;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Use config git credentials if no override provided
|
|
238
|
-
data.username = gitUsername || cfg.gitUsername;
|
|
239
|
-
data.password = gitPassword || cfg.gitPassword;
|
|
240
|
-
|
|
241
|
-
logger.info('Starting pull operation', { repoUrl, branch, transportRequest, service: 'abapgit-agent' });
|
|
242
|
-
|
|
243
|
-
return await this.request('POST', '/pull', data, { csrfToken: this.csrfToken });
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Health check
|
|
248
|
-
*/
|
|
249
|
-
async healthCheck() {
|
|
250
|
-
try {
|
|
251
|
-
const result = await this.request('GET', '/health');
|
|
252
|
-
return { status: 'healthy', abap: 'connected', ...result };
|
|
253
|
-
} catch (error) {
|
|
254
|
-
return { status: 'unhealthy', abap: 'disconnected', error: error.message };
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Check syntax of an ABAP object
|
|
260
|
-
*/
|
|
261
|
-
async syntaxCheck(objectType, objectName) {
|
|
262
|
-
// Fetch CSRF token first
|
|
263
|
-
await this.fetchCsrfToken();
|
|
264
|
-
|
|
265
|
-
const data = {
|
|
266
|
-
object_type: objectType,
|
|
267
|
-
object_name: objectName
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
logger.info('Starting syntax check', { objectType, objectName, service: 'abapgit-agent' });
|
|
271
|
-
|
|
272
|
-
return await this.request('POST', '/syntax-check', data, { csrfToken: this.csrfToken });
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Run unit tests for package or objects
|
|
277
|
-
* @param {string} packageName - Package name to run tests for (optional)
|
|
278
|
-
* @param {Array} objects - Array of {object_type, object_name} objects (optional)
|
|
279
|
-
* @returns {object} Unit test results
|
|
280
|
-
*/
|
|
281
|
-
async unitTest(packageName = null, objects = []) {
|
|
282
|
-
// Fetch CSRF token first
|
|
283
|
-
await this.fetchCsrfToken();
|
|
284
|
-
|
|
285
|
-
const data = {};
|
|
286
|
-
|
|
287
|
-
if (packageName) {
|
|
288
|
-
data.package = packageName;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (objects && objects.length > 0) {
|
|
292
|
-
data.objects = objects;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
logger.info('Starting unit tests', { package: packageName, objects, service: 'abapgit-agent' });
|
|
296
|
-
|
|
297
|
-
return await this.request('POST', '/unit', data, { csrfToken: this.csrfToken });
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Create a new online repository
|
|
302
|
-
* @param {string} repoUrl - Git repository URL
|
|
303
|
-
* @param {string} packageName - ABAP package name
|
|
304
|
-
* @param {string} branch - Branch name (default: 'main')
|
|
305
|
-
* @param {string} displayName - Display name for the repository (optional)
|
|
306
|
-
* @param {string} name - Repository name (optional)
|
|
307
|
-
* @param {string} folderLogic - Folder logic: 'PREFIX' or 'FULL' (default: 'PREFIX')
|
|
308
|
-
* @returns {object} Create result
|
|
309
|
-
*/
|
|
310
|
-
async create(repoUrl, packageName, branch = 'main', displayName = null, name = null, folderLogic = 'PREFIX') {
|
|
311
|
-
// Fetch CSRF token first
|
|
312
|
-
await this.fetchCsrfToken();
|
|
313
|
-
|
|
314
|
-
const data = {
|
|
315
|
-
url: repoUrl,
|
|
316
|
-
package: packageName,
|
|
317
|
-
branch: branch
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
if (displayName) {
|
|
321
|
-
data.display_name = displayName;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (name) {
|
|
325
|
-
data.name = name;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (folderLogic) {
|
|
329
|
-
data.folder_logic = folderLogic;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
logger.info('Creating repository', { repoUrl, packageName, branch, service: 'abapgit-agent' });
|
|
333
|
-
|
|
334
|
-
return await this.request('POST', '/create', data, { csrfToken: this.csrfToken });
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Import existing objects from package to git repository
|
|
339
|
-
* @param {string} repoUrl - Git repository URL
|
|
340
|
-
* @param {string} message - Commit message (optional)
|
|
341
|
-
* @returns {object} Import result with success, files_staged, commit_sha
|
|
342
|
-
*/
|
|
343
|
-
async import(repoUrl, message = null) {
|
|
344
|
-
// Fetch CSRF token first
|
|
345
|
-
await this.fetchCsrfToken();
|
|
346
|
-
|
|
347
|
-
const data = {
|
|
348
|
-
url: repoUrl
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
if (message) {
|
|
352
|
-
data.message = message;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
logger.info('Starting import operation', { repoUrl, message, service: 'abapgit-agent' });
|
|
356
|
-
|
|
357
|
-
return await this.request('POST', '/import', data, { csrfToken: this.csrfToken });
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Get package hierarchy tree
|
|
362
|
-
* @param {string} packageName - ABAP package name
|
|
363
|
-
* @param {number} depth - Maximum depth to traverse (default: 3, max: 10)
|
|
364
|
-
* @param {boolean} includeObjects - Include object counts breakdown
|
|
365
|
-
* @returns {object} Tree result with hierarchy and summary
|
|
366
|
-
*/
|
|
367
|
-
async tree(packageName, depth = 3, includeObjects = false) {
|
|
368
|
-
// Fetch CSRF token first
|
|
369
|
-
await this.fetchCsrfToken();
|
|
370
|
-
|
|
371
|
-
const data = {
|
|
372
|
-
package: packageName,
|
|
373
|
-
depth: Math.min(Math.max(1, depth), 10),
|
|
374
|
-
include_objects: includeObjects
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
logger.info('Getting package tree', { package: packageName, depth: data.depth, includeObjects, service: 'abapgit-agent' });
|
|
378
|
-
|
|
379
|
-
return await this.request('POST', '/tree', data, { csrfToken: this.csrfToken });
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
async preview(objects, type = null, limit = 100, offset = 0, where = null, columns = null) {
|
|
383
|
-
// Fetch CSRF token first
|
|
384
|
-
await this.fetchCsrfToken();
|
|
385
|
-
|
|
386
|
-
const data = {
|
|
387
|
-
objects: objects,
|
|
388
|
-
limit: Math.min(Math.max(1, limit), 500),
|
|
389
|
-
offset: Math.max(0, offset)
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
if (type) {
|
|
393
|
-
data.type = type;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (where) {
|
|
397
|
-
// Convert ISO date format (YYYY-MM-DD) to ABAP DATS format (YYYYMMDD)
|
|
398
|
-
// This handles date literals in WHERE clauses like "FLDATE = '2024-10-24'"
|
|
399
|
-
data.where = this.convertDatesInWhereClause(where);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (columns) {
|
|
403
|
-
data.columns = columns;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
logger.info('Previewing data', { objects, type, limit: data.limit, offset: data.offset, where: data.where, service: 'abapgit-agent' });
|
|
407
|
-
|
|
408
|
-
return await this.request('POST', '/preview', data, { csrfToken: this.csrfToken });
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Convert ISO date formats to ABAP DATS format in WHERE clause
|
|
413
|
-
* @param {string} whereClause - SQL WHERE clause
|
|
414
|
-
* @returns {string} - WHERE clause with dates converted to YYYYMMDD format
|
|
415
|
-
*/
|
|
416
|
-
convertDatesInWhereClause(whereClause) {
|
|
417
|
-
if (!whereClause) return whereClause;
|
|
418
|
-
|
|
419
|
-
// Pattern to match ISO date format: 'YYYY-MM-DD'
|
|
420
|
-
// Uses negative lookbehind and lookahead to ensure we're matching complete dates
|
|
421
|
-
const isoDatePattern = /'\d{4}-\d{2}-\d{2}'/g;
|
|
422
|
-
|
|
423
|
-
return whereClause.replace(isoDatePattern, (match) => {
|
|
424
|
-
// Extract YYYY, MM, DD from 'YYYY-MM-DD'
|
|
425
|
-
const dateContent = match.slice(1, -1); // Remove quotes: YYYY-MM-DD
|
|
426
|
-
const [year, month, day] = dateContent.split('-');
|
|
427
|
-
// Return in ABAP format: 'YYYYMMDD'
|
|
428
|
-
return `'${year}${month}${day}'`;
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* List objects in an ABAP package
|
|
434
|
-
* @param {string} packageName - ABAP package name
|
|
435
|
-
* @param {string} type - Comma-separated object types to filter (optional, e.g., 'CLAS,INTF')
|
|
436
|
-
* @param {string} name - Name pattern to filter (optional)
|
|
437
|
-
* @param {number} limit - Maximum number of objects to return (default: 100, max: 1000)
|
|
438
|
-
* @param {number} offset - Offset for pagination (default: 0)
|
|
439
|
-
* @returns {object} List result with objects, by_type, and total
|
|
440
|
-
*/
|
|
441
|
-
async list(packageName, type = null, name = null, limit = 100, offset = 0) {
|
|
442
|
-
// Fetch CSRF token first
|
|
443
|
-
await this.fetchCsrfToken();
|
|
444
|
-
|
|
445
|
-
const data = {
|
|
446
|
-
package: packageName,
|
|
447
|
-
limit: Math.min(Math.max(1, limit), 1000),
|
|
448
|
-
offset: Math.max(0, offset)
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
if (type) {
|
|
452
|
-
data.type = type;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (name) {
|
|
456
|
-
data.name = name;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
logger.info('Listing objects', { package: packageName, type, name, limit: data.limit, offset: data.offset, service: 'abapgit-agent' });
|
|
460
|
-
|
|
461
|
-
return await this.request('POST', '/list', data, { csrfToken: this.csrfToken });
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* View ABAP object definitions
|
|
466
|
-
* @param {Array} objects - Array of object names to view
|
|
467
|
-
* @param {string} type - Object type (optional, e.g., 'CLAS', 'TABL')
|
|
468
|
-
* @returns {object} View result with object definitions
|
|
469
|
-
*/
|
|
470
|
-
async view(objects, type = null) {
|
|
471
|
-
await this.fetchCsrfToken();
|
|
472
|
-
|
|
473
|
-
const data = {
|
|
474
|
-
objects: objects
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
if (type) {
|
|
478
|
-
data.type = type;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
logger.info('Viewing objects', { objects, type, service: 'abapgit-agent' });
|
|
482
|
-
|
|
483
|
-
return await this.request('POST', '/view', data, { csrfToken: this.csrfToken });
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* Find where-used list for ABAP objects
|
|
488
|
-
* @param {Array} objects - Array of object names to search
|
|
489
|
-
* @param {string} type - Object type (optional)
|
|
490
|
-
* @param {number} limit - Maximum results (default: 100, max: 500)
|
|
491
|
-
* @param {number} offset - Number of results to skip (default: 0)
|
|
492
|
-
* @returns {object} Where-used result with found objects
|
|
493
|
-
*/
|
|
494
|
-
async where(objects, type = null, limit = 100, offset = 0) {
|
|
495
|
-
await this.fetchCsrfToken();
|
|
496
|
-
|
|
497
|
-
const data = {
|
|
498
|
-
objects: objects,
|
|
499
|
-
limit: Math.min(Math.max(1, limit), 500),
|
|
500
|
-
offset: Math.max(0, offset)
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
if (type) {
|
|
504
|
-
data.type = type;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
logger.info('Finding where-used', { objects, type, limit: data.limit, offset: data.offset, service: 'abapgit-agent' });
|
|
508
|
-
|
|
509
|
-
return await this.request('POST', '/where', data, { csrfToken: this.csrfToken });
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// Singleton instance
|
|
514
|
-
let instance = null;
|
|
515
|
-
|
|
516
|
-
function getClient() {
|
|
517
|
-
if (!instance) {
|
|
518
|
-
instance = new ABAPClient();
|
|
519
|
-
}
|
|
520
|
-
return instance;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
module.exports = {
|
|
524
|
-
ABAPClient,
|
|
525
|
-
getClient
|
|
526
|
-
};
|
|
File without changes
|