@mcp-abap-adt/adt-clients 7.2.0 → 7.3.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/dist/clients/AdtClient.d.ts +12 -0
- package/dist/clients/AdtClient.d.ts.map +1 -1
- package/dist/clients/AdtClient.js +16 -0
- package/dist/constants/contentTypes.d.ts +1 -0
- package/dist/constants/contentTypes.d.ts.map +1 -1
- package/dist/constants/contentTypes.js +3 -1
- package/dist/core/messageClass/AdtMessageClass.d.ts +82 -0
- package/dist/core/messageClass/AdtMessageClass.d.ts.map +1 -0
- package/dist/core/messageClass/AdtMessageClass.js +246 -0
- package/dist/core/messageClass/AdtMessageClassMessage.d.ts +76 -0
- package/dist/core/messageClass/AdtMessageClassMessage.d.ts.map +1 -0
- package/dist/core/messageClass/AdtMessageClassMessage.js +314 -0
- package/dist/core/messageClass/create.d.ts +15 -0
- package/dist/core/messageClass/create.d.ts.map +1 -0
- package/dist/core/messageClass/create.js +39 -0
- package/dist/core/messageClass/delete.d.ts +25 -0
- package/dist/core/messageClass/delete.d.ts.map +1 -0
- package/dist/core/messageClass/delete.js +68 -0
- package/dist/core/messageClass/index.d.ts +13 -0
- package/dist/core/messageClass/index.d.ts.map +1 -0
- package/dist/core/messageClass/index.js +28 -0
- package/dist/core/messageClass/lock.d.ts +26 -0
- package/dist/core/messageClass/lock.d.ts.map +1 -0
- package/dist/core/messageClass/lock.js +82 -0
- package/dist/core/messageClass/read.d.ts +10 -0
- package/dist/core/messageClass/read.d.ts.map +1 -0
- package/dist/core/messageClass/read.js +24 -0
- package/dist/core/messageClass/types.d.ts +53 -0
- package/dist/core/messageClass/types.d.ts.map +1 -0
- package/dist/core/messageClass/types.js +5 -0
- package/dist/core/messageClass/unlock.d.ts +16 -0
- package/dist/core/messageClass/unlock.d.ts.map +1 -0
- package/dist/core/messageClass/unlock.js +39 -0
- package/dist/core/messageClass/update.d.ts +16 -0
- package/dist/core/messageClass/update.d.ts.map +1 -0
- package/dist/core/messageClass/update.js +44 -0
- package/dist/core/messageClass/xml.d.ts +24 -0
- package/dist/core/messageClass/xml.d.ts.map +1 -0
- package/dist/core/messageClass/xml.js +107 -0
- package/dist/core/shared/unsupported.d.ts +3 -0
- package/dist/core/shared/unsupported.d.ts.map +1 -0
- package/dist/core/shared/unsupported.js +10 -0
- package/dist/index.core.d.ts +2 -0
- package/dist/index.core.d.ts.map +1 -1
- package/dist/index.core.js +4 -1
- package/package.json +4 -4
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AdtMessageClassMessage — read-modify-write operations for a single message
|
|
4
|
+
* within a Message Class (MSAG/N).
|
|
5
|
+
*
|
|
6
|
+
* Implements IAdtObject<IMessageClassMessageConfig, IMessageClassMessageState>.
|
|
7
|
+
*
|
|
8
|
+
* Operation chains:
|
|
9
|
+
* - read: GET class XML → find message by msgno → return state.message
|
|
10
|
+
* - create/update: GET class → merge message → stateful → lockMessage (MH) +
|
|
11
|
+
* lockClassForMessage (CH) → PUT full class XML (message with
|
|
12
|
+
* mc:lockhandle=MH, lockHandle=CH) → unlockAllMessages →
|
|
13
|
+
* unlock class (CH) → stateless
|
|
14
|
+
* - delete: GET class → stateful → lockMessage (MH) + lockClassForMessage (CH)
|
|
15
|
+
* → PUT class XML with target message as <mc:deletedmessages
|
|
16
|
+
* mc:lockhandle=MH>, all other messages as <mc:messages> →
|
|
17
|
+
* unlockAllMessages → unlock class (CH) → stateless.
|
|
18
|
+
* (SAP does NOT delete omitted messages on PUT — <mc:deletedmessages>
|
|
19
|
+
* is the correct mechanism. A message-level DELETE /messages/{no}
|
|
20
|
+
* returns 423 and is NOT used.)
|
|
21
|
+
*
|
|
22
|
+
* Unsupported: activate, check, validate, lock, unlock, getVersions,
|
|
23
|
+
* getVersionSource, readTransport → throwUnsupportedOperation.
|
|
24
|
+
*
|
|
25
|
+
* transport / corrNr: not sent yet — Task 6.2 will wire it.
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.AdtMessageClassMessage = void 0;
|
|
29
|
+
const interfaces_1 = require("@mcp-abap-adt/interfaces");
|
|
30
|
+
const contentTypes_1 = require("../../constants/contentTypes");
|
|
31
|
+
const internalUtils_1 = require("../../utils/internalUtils");
|
|
32
|
+
const timeouts_1 = require("../../utils/timeouts");
|
|
33
|
+
const unsupported_1 = require("../shared/unsupported");
|
|
34
|
+
const lock_1 = require("./lock");
|
|
35
|
+
const read_1 = require("./read");
|
|
36
|
+
const unlock_1 = require("./unlock");
|
|
37
|
+
const xml_1 = require("./xml");
|
|
38
|
+
const BASE = '/sap/bc/adt/messageclass';
|
|
39
|
+
class AdtMessageClassMessage {
|
|
40
|
+
connection;
|
|
41
|
+
logger;
|
|
42
|
+
objectType = 'MessageClassMessage';
|
|
43
|
+
constructor(connection, logger) {
|
|
44
|
+
this.connection = connection;
|
|
45
|
+
this.logger = logger;
|
|
46
|
+
}
|
|
47
|
+
// ── read ──────────────────────────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Read a single message from the parent class.
|
|
50
|
+
* Returns undefined when the parent class itself is absent (404).
|
|
51
|
+
* Throws OBJECT_NOT_FOUND when the class exists but the message number is absent.
|
|
52
|
+
*/
|
|
53
|
+
async read(config) {
|
|
54
|
+
if (!config.className)
|
|
55
|
+
throw new Error('className is required');
|
|
56
|
+
if (!config.msgno)
|
|
57
|
+
throw new Error('msgno is required');
|
|
58
|
+
const no = String(config.msgno);
|
|
59
|
+
try {
|
|
60
|
+
const response = await (0, read_1.getMessageClassSource)(this.connection, config.className);
|
|
61
|
+
const cls = (0, xml_1.parseMessageClass)(String(response.data));
|
|
62
|
+
const message = cls.messages.find((m) => m.msgno === no);
|
|
63
|
+
if (!message) {
|
|
64
|
+
const e = new interfaces_1.AdtOperationError(`Message ${no} not found in class ${config.className}`);
|
|
65
|
+
e.code = interfaces_1.AdtObjectErrorCodes.OBJECT_NOT_FOUND;
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
return { message, errors: [] };
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
const e = error;
|
|
72
|
+
if (e.response?.status === 404) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ── create / update (upsert) ───────────────────────────────────────────────
|
|
79
|
+
/**
|
|
80
|
+
* Create or upsert a single message in the parent class.
|
|
81
|
+
* Delegates to the update logic.
|
|
82
|
+
*/
|
|
83
|
+
async create(config, _options) {
|
|
84
|
+
return this._upsertMessage(config);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Update (upsert) a single message in the parent class.
|
|
88
|
+
* Full chain: GET class → merge message → stateful → LOCK_MSG + class LOCK →
|
|
89
|
+
* PUT class XML → unlock class → unlockAllMessages → stateless.
|
|
90
|
+
*/
|
|
91
|
+
async update(config, _options) {
|
|
92
|
+
return this._upsertMessage(config);
|
|
93
|
+
}
|
|
94
|
+
async _upsertMessage(config) {
|
|
95
|
+
if (!config.className)
|
|
96
|
+
throw new Error('className is required');
|
|
97
|
+
if (!config.msgno)
|
|
98
|
+
throw new Error('msgno is required');
|
|
99
|
+
const name = config.className;
|
|
100
|
+
const no = String(config.msgno);
|
|
101
|
+
// 1. Read current class state to preserve all messages
|
|
102
|
+
const response = await (0, read_1.getMessageClassSource)(this.connection, name);
|
|
103
|
+
const cls = (0, xml_1.parseMessageClass)(String(response.data));
|
|
104
|
+
// 2. Merge/set the message in the messages array
|
|
105
|
+
const existingIdx = cls.messages.findIndex((m) => m.msgno === no);
|
|
106
|
+
if (existingIdx >= 0) {
|
|
107
|
+
// Update existing message — only override fields when explicitly provided in config
|
|
108
|
+
cls.messages[existingIdx] = {
|
|
109
|
+
...cls.messages[existingIdx],
|
|
110
|
+
...(config.msgtext !== undefined ? { msgtext: config.msgtext } : {}),
|
|
111
|
+
...(config.selfExplanatory !== undefined
|
|
112
|
+
? { selfExplanatory: config.selfExplanatory }
|
|
113
|
+
: {}),
|
|
114
|
+
...(config.description !== undefined
|
|
115
|
+
? { description: config.description }
|
|
116
|
+
: {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Add new message with all authorable fields
|
|
121
|
+
cls.messages.push({
|
|
122
|
+
msgno: no,
|
|
123
|
+
msgtext: config.msgtext ?? '',
|
|
124
|
+
...(config.selfExplanatory !== undefined
|
|
125
|
+
? { selfExplanatory: config.selfExplanatory }
|
|
126
|
+
: {}),
|
|
127
|
+
...(config.description !== undefined
|
|
128
|
+
? { description: config.description }
|
|
129
|
+
: {}),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
let messageLockHandle;
|
|
133
|
+
let classLockHandle;
|
|
134
|
+
try {
|
|
135
|
+
this.logger?.info?.('upsertMessage: stateful');
|
|
136
|
+
this.connection.setSessionType('stateful');
|
|
137
|
+
// 3. Lock individual message
|
|
138
|
+
this.logger?.info?.('upsertMessage: lockMessage');
|
|
139
|
+
messageLockHandle = await (0, lock_1.lockMessage)(this.connection, name, no);
|
|
140
|
+
// 4. Lock class for message save
|
|
141
|
+
this.logger?.info?.('upsertMessage: lockClassForMessage');
|
|
142
|
+
classLockHandle = await (0, lock_1.lockClassForMessage)(this.connection, name, no);
|
|
143
|
+
// 5. PUT full class XML with message lock handle embedded
|
|
144
|
+
this.logger?.info?.('upsertMessage: PUT');
|
|
145
|
+
const xmlBody = (0, xml_1.buildMessageClassXml)(cls, {
|
|
146
|
+
messageLockHandles: { [no]: messageLockHandle },
|
|
147
|
+
});
|
|
148
|
+
const encoded = (0, internalUtils_1.encodeSapObjectName)(name.toLowerCase());
|
|
149
|
+
const updateResult = await this.connection.makeAdtRequest({
|
|
150
|
+
url: `${BASE}/${encoded}?lockHandle=${encodeURIComponent(classLockHandle)}`,
|
|
151
|
+
method: 'PUT',
|
|
152
|
+
timeout: (0, timeouts_1.getTimeout)('default'),
|
|
153
|
+
data: xmlBody,
|
|
154
|
+
headers: { 'Content-Type': contentTypes_1.MESSAGE_CLASS_UPDATE_CONTENT_TYPE },
|
|
155
|
+
});
|
|
156
|
+
// 6. Release the message lock first, then unlock the whole class last —
|
|
157
|
+
// the class edit-lock must be the final release of the process.
|
|
158
|
+
this.logger?.info?.('upsertMessage: unlockAllMessages');
|
|
159
|
+
await (0, unlock_1.unlockAllMessages)(this.connection, name, no);
|
|
160
|
+
messageLockHandle = undefined;
|
|
161
|
+
// 7. Unlock the whole class (final release)
|
|
162
|
+
this.logger?.info?.('upsertMessage: unlock class');
|
|
163
|
+
await (0, unlock_1.unlockMessageClass)(this.connection, name, classLockHandle);
|
|
164
|
+
classLockHandle = undefined;
|
|
165
|
+
// 8. Back to stateless
|
|
166
|
+
this.connection.setSessionType('stateless');
|
|
167
|
+
this.logger?.info?.('upsertMessage: done');
|
|
168
|
+
return { updateResult, errors: [] };
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
// Always clean up locks and reset session on failure — release the
|
|
172
|
+
// message lock first, then the class (final release), mirroring the happy path.
|
|
173
|
+
if (messageLockHandle) {
|
|
174
|
+
try {
|
|
175
|
+
await (0, unlock_1.unlockAllMessages)(this.connection, name, no);
|
|
176
|
+
}
|
|
177
|
+
catch (ue) {
|
|
178
|
+
this.logger?.warn?.('Failed to unlock messages during cleanup:', (0, internalUtils_1.safeErrorMessage)(ue));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (classLockHandle) {
|
|
182
|
+
try {
|
|
183
|
+
await (0, unlock_1.unlockMessageClass)(this.connection, name, classLockHandle);
|
|
184
|
+
}
|
|
185
|
+
catch (ue) {
|
|
186
|
+
this.logger?.warn?.('Failed to unlock class during cleanup:', (0, internalUtils_1.safeErrorMessage)(ue));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.connection.setSessionType('stateless');
|
|
190
|
+
this.logger?.error('upsertMessage failed:', (0, internalUtils_1.safeErrorMessage)(error));
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// ── delete ────────────────────────────────────────────────────────────────
|
|
195
|
+
/**
|
|
196
|
+
* Delete a single message from the parent class.
|
|
197
|
+
*
|
|
198
|
+
* SAP does NOT remove messages that are merely omitted from a class PUT — it
|
|
199
|
+
* only upserts what is present. The correct mechanism is to PUT the class
|
|
200
|
+
* with the target message placed in <mc:deletedmessages> (carrying its own
|
|
201
|
+
* message lock handle), while every other message remains in <mc:messages>.
|
|
202
|
+
*
|
|
203
|
+
* Chain: GET class → stateful → lockMessage (MH) + lockClassForMessage (CH)
|
|
204
|
+
* → PUT class XML with target in <mc:deletedmessages mc:lockhandle=MH>,
|
|
205
|
+
* remaining messages in <mc:messages> → unlockAllMessages →
|
|
206
|
+
* unlock class (CH) → stateless.
|
|
207
|
+
*/
|
|
208
|
+
async delete(config) {
|
|
209
|
+
if (!config.className)
|
|
210
|
+
throw new Error('className is required');
|
|
211
|
+
if (!config.msgno)
|
|
212
|
+
throw new Error('msgno is required');
|
|
213
|
+
const name = config.className;
|
|
214
|
+
const no = String(config.msgno);
|
|
215
|
+
// 1. Read current class state — keep ALL messages (including the one being
|
|
216
|
+
// deleted) so the builder can emit <mc:deletedmessages> for the target
|
|
217
|
+
const response = await (0, read_1.getMessageClassSource)(this.connection, name);
|
|
218
|
+
const cls = (0, xml_1.parseMessageClass)(String(response.data));
|
|
219
|
+
let messageLockHandle;
|
|
220
|
+
let classLockHandle;
|
|
221
|
+
try {
|
|
222
|
+
this.logger?.info?.('deleteMessage: stateful');
|
|
223
|
+
this.connection.setSessionType('stateful');
|
|
224
|
+
// 2. Lock the individual message (required for <mc:deletedmessages>)
|
|
225
|
+
this.logger?.info?.('deleteMessage: lockMessage');
|
|
226
|
+
messageLockHandle = await (0, lock_1.lockMessage)(this.connection, name, no);
|
|
227
|
+
// 3. Lock the class for a message-save (msgNo + onSave=X)
|
|
228
|
+
this.logger?.info?.('deleteMessage: lockClassForMessage');
|
|
229
|
+
classLockHandle = await (0, lock_1.lockClassForMessage)(this.connection, name, no);
|
|
230
|
+
// 4. PUT: target message → <mc:deletedmessages>; all others → <mc:messages>
|
|
231
|
+
this.logger?.info?.('deleteMessage: PUT');
|
|
232
|
+
const xmlBody = (0, xml_1.buildMessageClassXml)(cls, {
|
|
233
|
+
deletedMsgnos: [no],
|
|
234
|
+
messageLockHandles: { [no]: messageLockHandle },
|
|
235
|
+
});
|
|
236
|
+
const encoded = (0, internalUtils_1.encodeSapObjectName)(name.toLowerCase());
|
|
237
|
+
const deleteResult = await this.connection.makeAdtRequest({
|
|
238
|
+
url: `${BASE}/${encoded}?lockHandle=${encodeURIComponent(classLockHandle)}`,
|
|
239
|
+
method: 'PUT',
|
|
240
|
+
timeout: (0, timeouts_1.getTimeout)('default'),
|
|
241
|
+
data: xmlBody,
|
|
242
|
+
headers: { 'Content-Type': contentTypes_1.MESSAGE_CLASS_UPDATE_CONTENT_TYPE },
|
|
243
|
+
});
|
|
244
|
+
// 5. Release the message lock first, then unlock the whole class last.
|
|
245
|
+
this.logger?.info?.('deleteMessage: unlockAllMessages');
|
|
246
|
+
await (0, unlock_1.unlockAllMessages)(this.connection, name, no);
|
|
247
|
+
messageLockHandle = undefined;
|
|
248
|
+
// 6. Unlock the whole class (final release)
|
|
249
|
+
this.logger?.info?.('deleteMessage: unlock class');
|
|
250
|
+
await (0, unlock_1.unlockMessageClass)(this.connection, name, classLockHandle);
|
|
251
|
+
classLockHandle = undefined;
|
|
252
|
+
this.connection.setSessionType('stateless');
|
|
253
|
+
this.logger?.info?.('deleteMessage: done');
|
|
254
|
+
return { deleteResult, errors: [] };
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
// Release the message lock first, then the class (final release).
|
|
258
|
+
if (messageLockHandle) {
|
|
259
|
+
try {
|
|
260
|
+
await (0, unlock_1.unlockAllMessages)(this.connection, name, no);
|
|
261
|
+
}
|
|
262
|
+
catch (ue) {
|
|
263
|
+
this.logger?.warn?.('Failed to unlock messages during cleanup:', (0, internalUtils_1.safeErrorMessage)(ue));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (classLockHandle) {
|
|
267
|
+
try {
|
|
268
|
+
await (0, unlock_1.unlockMessageClass)(this.connection, name, classLockHandle);
|
|
269
|
+
}
|
|
270
|
+
catch (ue) {
|
|
271
|
+
this.logger?.warn?.('Failed to unlock class during cleanup:', (0, internalUtils_1.safeErrorMessage)(ue));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
this.connection.setSessionType('stateless');
|
|
275
|
+
this.logger?.error('deleteMessage failed:', (0, internalUtils_1.safeErrorMessage)(error));
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// ── readMetadata ───────────────────────────────────────────────────────────
|
|
280
|
+
/** Delegates to read() — individual messages have no separate metadata endpoint. */
|
|
281
|
+
async readMetadata(config) {
|
|
282
|
+
const state = await this.read(config);
|
|
283
|
+
if (!state) {
|
|
284
|
+
throw new Error(`Message ${config.msgno} not found in class ${config.className}`);
|
|
285
|
+
}
|
|
286
|
+
return state;
|
|
287
|
+
}
|
|
288
|
+
// ── unsupported operations ─────────────────────────────────────────────────
|
|
289
|
+
async validate(_config) {
|
|
290
|
+
(0, unsupported_1.throwUnsupportedOperation)('validate', 'message class message');
|
|
291
|
+
}
|
|
292
|
+
async activate(_config) {
|
|
293
|
+
(0, unsupported_1.throwUnsupportedOperation)('activate', 'message class message');
|
|
294
|
+
}
|
|
295
|
+
async check(_config) {
|
|
296
|
+
(0, unsupported_1.throwUnsupportedOperation)('check', 'message class message');
|
|
297
|
+
}
|
|
298
|
+
async readTransport(_config) {
|
|
299
|
+
(0, unsupported_1.throwUnsupportedOperation)('readTransport', 'message class message');
|
|
300
|
+
}
|
|
301
|
+
async lock(_config) {
|
|
302
|
+
(0, unsupported_1.throwUnsupportedOperation)('lock', 'message class message');
|
|
303
|
+
}
|
|
304
|
+
async unlock(_config, _lockHandle) {
|
|
305
|
+
(0, unsupported_1.throwUnsupportedOperation)('unlock', 'message class message');
|
|
306
|
+
}
|
|
307
|
+
async getVersions(_config) {
|
|
308
|
+
(0, unsupported_1.throwUnsupportedOperation)('getVersions', 'message class message');
|
|
309
|
+
}
|
|
310
|
+
async getVersionSource(_contentUri) {
|
|
311
|
+
(0, unsupported_1.throwUnsupportedOperation)('getVersionSource', 'message class message');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
exports.AdtMessageClassMessage = AdtMessageClassMessage;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message class create operations
|
|
3
|
+
*/
|
|
4
|
+
import type { IAbapConnection, IAdtResponse } from '@mcp-abap-adt/interfaces';
|
|
5
|
+
import type { ICreateMessageClassParams } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Create a new message class (shell — no messages yet).
|
|
8
|
+
* POST /sap/bc/adt/messageclass with Content-Type application/xml.
|
|
9
|
+
*
|
|
10
|
+
* corrNr (transport) is intentionally omitted here — the local/no-transport
|
|
11
|
+
* path is the only one confirmed by probe. Task 6.2 will wire it for
|
|
12
|
+
* transportable packages once a corrNr probe succeeds.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createMessageClass(connection: IAbapConnection, params: ICreateMessageClassParams): Promise<IAdtResponse>;
|
|
15
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/core/messageClass/create.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE9E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAKzD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,YAAY,CAAC,CAsBvB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Message class create operations
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createMessageClass = createMessageClass;
|
|
7
|
+
const timeouts_1 = require("../../utils/timeouts");
|
|
8
|
+
const xml_1 = require("./xml");
|
|
9
|
+
const BASE = '/sap/bc/adt/messageclass';
|
|
10
|
+
/**
|
|
11
|
+
* Create a new message class (shell — no messages yet).
|
|
12
|
+
* POST /sap/bc/adt/messageclass with Content-Type application/xml.
|
|
13
|
+
*
|
|
14
|
+
* corrNr (transport) is intentionally omitted here — the local/no-transport
|
|
15
|
+
* path is the only one confirmed by probe. Task 6.2 will wire it for
|
|
16
|
+
* transportable packages once a corrNr probe succeeds.
|
|
17
|
+
*/
|
|
18
|
+
async function createMessageClass(connection, params) {
|
|
19
|
+
// Emit BOTH adtcore:language and adtcore:masterLanguage (like domain/class),
|
|
20
|
+
// both set to the resolved language.
|
|
21
|
+
const lang = params.master_language ?? 'EN';
|
|
22
|
+
const xmlBody = (0, xml_1.buildMessageClassXml)({
|
|
23
|
+
name: params.name.toUpperCase(),
|
|
24
|
+
description: params.description,
|
|
25
|
+
packageName: params.package_name.toUpperCase(),
|
|
26
|
+
language: lang,
|
|
27
|
+
masterLanguage: lang,
|
|
28
|
+
messages: [],
|
|
29
|
+
});
|
|
30
|
+
return connection.makeAdtRequest({
|
|
31
|
+
url: BASE,
|
|
32
|
+
method: 'POST',
|
|
33
|
+
timeout: (0, timeouts_1.getTimeout)('default'),
|
|
34
|
+
data: xmlBody,
|
|
35
|
+
headers: {
|
|
36
|
+
'Content-Type': 'application/xml',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message class delete operations.
|
|
3
|
+
*
|
|
4
|
+
* Uses the stateless ADT deletion service (`/sap/bc/adt/deletion/check` +
|
|
5
|
+
* `/sap/bc/adt/deletion/delete`) — the same mechanism Eclipse ADT and the other
|
|
6
|
+
* object types (domain, serviceDefinition, …) use. A direct
|
|
7
|
+
* `DELETE /messageclass/{name}?lockHandle=` leaves a lingering message-editing
|
|
8
|
+
* enqueue ("User is currently editing …") that blocks a same-name re-create, so
|
|
9
|
+
* it is NOT used here.
|
|
10
|
+
*/
|
|
11
|
+
import type { IAbapConnection, IAdtResponse } from '@mcp-abap-adt/interfaces';
|
|
12
|
+
/**
|
|
13
|
+
* Low-level: check whether the message class can be deleted.
|
|
14
|
+
* POST /sap/bc/adt/deletion/check
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkDeletion(connection: IAbapConnection, name: string): Promise<IAdtResponse>;
|
|
17
|
+
/**
|
|
18
|
+
* Low-level: delete the message class via the deletion service.
|
|
19
|
+
* POST /sap/bc/adt/deletion/delete (stateless — no lock handle).
|
|
20
|
+
*
|
|
21
|
+
* corrNr / transport_request is intentionally omitted — local/no-transport path
|
|
22
|
+
* only, wired for transportable packages in the corrNr task.
|
|
23
|
+
*/
|
|
24
|
+
export declare function deleteMessageClass(connection: IAbapConnection, name: string): Promise<IAdtResponse>;
|
|
25
|
+
//# sourceMappingURL=delete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/core/messageClass/delete.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAe9E;;;GAGG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAkBvB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAoBvB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Message class delete operations.
|
|
4
|
+
*
|
|
5
|
+
* Uses the stateless ADT deletion service (`/sap/bc/adt/deletion/check` +
|
|
6
|
+
* `/sap/bc/adt/deletion/delete`) — the same mechanism Eclipse ADT and the other
|
|
7
|
+
* object types (domain, serviceDefinition, …) use. A direct
|
|
8
|
+
* `DELETE /messageclass/{name}?lockHandle=` leaves a lingering message-editing
|
|
9
|
+
* enqueue ("User is currently editing …") that blocks a same-name re-create, so
|
|
10
|
+
* it is NOT used here.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.checkDeletion = checkDeletion;
|
|
14
|
+
exports.deleteMessageClass = deleteMessageClass;
|
|
15
|
+
const contentTypes_1 = require("../../constants/contentTypes");
|
|
16
|
+
const internalUtils_1 = require("../../utils/internalUtils");
|
|
17
|
+
const timeouts_1 = require("../../utils/timeouts");
|
|
18
|
+
const BASE = '/sap/bc/adt/messageclass';
|
|
19
|
+
const objectUri = (name) => `${BASE}/${(0, internalUtils_1.encodeSapObjectName)(name.toLowerCase())}`;
|
|
20
|
+
/**
|
|
21
|
+
* Low-level: check whether the message class can be deleted.
|
|
22
|
+
* POST /sap/bc/adt/deletion/check
|
|
23
|
+
*/
|
|
24
|
+
async function checkDeletion(connection, name) {
|
|
25
|
+
if (!name)
|
|
26
|
+
throw new Error('name is required');
|
|
27
|
+
const xmlPayload = `<?xml version="1.0" encoding="UTF-8"?>
|
|
28
|
+
<del:checkRequest xmlns:del="http://www.sap.com/adt/deletion" xmlns:adtcore="http://www.sap.com/adt/core">
|
|
29
|
+
<del:object adtcore:uri="${objectUri(name)}"/>
|
|
30
|
+
</del:checkRequest>`;
|
|
31
|
+
return connection.makeAdtRequest({
|
|
32
|
+
url: '/sap/bc/adt/deletion/check',
|
|
33
|
+
method: 'POST',
|
|
34
|
+
timeout: (0, timeouts_1.getTimeout)('default'),
|
|
35
|
+
data: xmlPayload,
|
|
36
|
+
headers: {
|
|
37
|
+
Accept: contentTypes_1.ACCEPT_DELETION_CHECK,
|
|
38
|
+
'Content-Type': contentTypes_1.CT_DELETION_CHECK,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Low-level: delete the message class via the deletion service.
|
|
44
|
+
* POST /sap/bc/adt/deletion/delete (stateless — no lock handle).
|
|
45
|
+
*
|
|
46
|
+
* corrNr / transport_request is intentionally omitted — local/no-transport path
|
|
47
|
+
* only, wired for transportable packages in the corrNr task.
|
|
48
|
+
*/
|
|
49
|
+
async function deleteMessageClass(connection, name) {
|
|
50
|
+
if (!name)
|
|
51
|
+
throw new Error('name is required');
|
|
52
|
+
const xmlPayload = `<?xml version="1.0" encoding="UTF-8"?>
|
|
53
|
+
<del:deletionRequest xmlns:del="http://www.sap.com/adt/deletion" xmlns:adtcore="http://www.sap.com/adt/core">
|
|
54
|
+
<del:object adtcore:uri="${objectUri(name)}">
|
|
55
|
+
<del:transportNumber/>
|
|
56
|
+
</del:object>
|
|
57
|
+
</del:deletionRequest>`;
|
|
58
|
+
return connection.makeAdtRequest({
|
|
59
|
+
url: '/sap/bc/adt/deletion/delete',
|
|
60
|
+
method: 'POST',
|
|
61
|
+
timeout: (0, timeouts_1.getTimeout)('default'),
|
|
62
|
+
data: xmlPayload,
|
|
63
|
+
headers: {
|
|
64
|
+
Accept: contentTypes_1.ACCEPT_DELETION,
|
|
65
|
+
'Content-Type': contentTypes_1.CT_DELETION,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message class operations - public exports
|
|
3
|
+
*/
|
|
4
|
+
import type { IAdtObject } from '@mcp-abap-adt/interfaces';
|
|
5
|
+
import type { IMessageClassConfig, IMessageClassMessageConfig, IMessageClassMessageState, IMessageClassState } from './types';
|
|
6
|
+
export { AdtMessageClass } from './AdtMessageClass';
|
|
7
|
+
export { AdtMessageClassMessage } from './AdtMessageClassMessage';
|
|
8
|
+
export * from './types';
|
|
9
|
+
export type { IParsedMessage, IParsedMessageClass } from './xml';
|
|
10
|
+
export { buildMessageClassXml, parseMessageClass } from './xml';
|
|
11
|
+
export type AdtMessageClassType = IAdtObject<IMessageClassConfig, IMessageClassState>;
|
|
12
|
+
export type AdtMessageClassMessageType = IAdtObject<IMessageClassMessageConfig, IMessageClassMessageState>;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/messageClass/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,yBAAyB,EACzB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,cAAc,SAAS,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAGhE,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAC1C,mBAAmB,EACnB,kBAAkB,CACnB,CAAC;AAGF,MAAM,MAAM,0BAA0B,GAAG,UAAU,CACjD,0BAA0B,EAC1B,yBAAyB,CAC1B,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Message class operations - public exports
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.parseMessageClass = exports.buildMessageClassXml = exports.AdtMessageClassMessage = exports.AdtMessageClass = void 0;
|
|
21
|
+
var AdtMessageClass_1 = require("./AdtMessageClass");
|
|
22
|
+
Object.defineProperty(exports, "AdtMessageClass", { enumerable: true, get: function () { return AdtMessageClass_1.AdtMessageClass; } });
|
|
23
|
+
var AdtMessageClassMessage_1 = require("./AdtMessageClassMessage");
|
|
24
|
+
Object.defineProperty(exports, "AdtMessageClassMessage", { enumerable: true, get: function () { return AdtMessageClassMessage_1.AdtMessageClassMessage; } });
|
|
25
|
+
__exportStar(require("./types"), exports);
|
|
26
|
+
var xml_1 = require("./xml");
|
|
27
|
+
Object.defineProperty(exports, "buildMessageClassXml", { enumerable: true, get: function () { return xml_1.buildMessageClassXml; } });
|
|
28
|
+
Object.defineProperty(exports, "parseMessageClass", { enumerable: true, get: function () { return xml_1.parseMessageClass; } });
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message class lock operations
|
|
3
|
+
*/
|
|
4
|
+
import type { IAbapConnection } from '@mcp-abap-adt/interfaces';
|
|
5
|
+
/**
|
|
6
|
+
* Lock a message class for modification.
|
|
7
|
+
* Returns the lock handle that must be used in subsequent update/delete requests.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: Caller must enable stateful session via connection.setSessionType('stateful') first.
|
|
10
|
+
*/
|
|
11
|
+
export declare function lockMessageClass(connection: IAbapConnection, name: string): Promise<string>;
|
|
12
|
+
/**
|
|
13
|
+
* Lock an individual message for modification.
|
|
14
|
+
* Returns the message lock handle (MH) used in PUT XML as mc:lockhandle.
|
|
15
|
+
*
|
|
16
|
+
* NOTE: Caller must enable stateful session via connection.setSessionType('stateful') first.
|
|
17
|
+
*/
|
|
18
|
+
export declare function lockMessage(connection: IAbapConnection, name: string, no: string): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Lock a message class in the context of a specific message save.
|
|
21
|
+
* Returns the class lock handle (CH) used in PUT ?lockHandle= parameter.
|
|
22
|
+
*
|
|
23
|
+
* NOTE: Caller must enable stateful session via connection.setSessionType('stateful') first.
|
|
24
|
+
*/
|
|
25
|
+
export declare function lockClassForMessage(connection: IAbapConnection, name: string, no: string): Promise<string>;
|
|
26
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../../src/core/messageClass/lock.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AA0BhE;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAajB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,MAAM,CAAC,CAajB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,MAAM,CAAC,CAajB"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Message class lock operations
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.lockMessageClass = lockMessageClass;
|
|
7
|
+
exports.lockMessage = lockMessage;
|
|
8
|
+
exports.lockClassForMessage = lockClassForMessage;
|
|
9
|
+
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
10
|
+
const contentTypes_1 = require("../../constants/contentTypes");
|
|
11
|
+
const internalUtils_1 = require("../../utils/internalUtils");
|
|
12
|
+
const timeouts_1 = require("../../utils/timeouts");
|
|
13
|
+
const BASE = '/sap/bc/adt/messageclass';
|
|
14
|
+
// Accept header for individual message lock (StatusMessage response type)
|
|
15
|
+
const ACCEPT_LOCK_MSG = 'application/vnd.sap.as+xml;charset=UTF-8;dataname=com.sap.adt.StatusMessage';
|
|
16
|
+
/** Parse LOCK_HANDLE from the asx:abap lock response XML. Throws with errLabel if absent. */
|
|
17
|
+
function parseLockHandle(data, errLabel) {
|
|
18
|
+
const parser = new fast_xml_parser_1.XMLParser({
|
|
19
|
+
ignoreAttributes: false,
|
|
20
|
+
attributeNamePrefix: '',
|
|
21
|
+
});
|
|
22
|
+
const result = parser.parse(data);
|
|
23
|
+
const lockHandle = result['asx:abap']?.['asx:values']?.DATA?.LOCK_HANDLE;
|
|
24
|
+
if (!lockHandle) {
|
|
25
|
+
throw new Error(`Failed to extract lock handle from ${errLabel}`);
|
|
26
|
+
}
|
|
27
|
+
return lockHandle;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Lock a message class for modification.
|
|
31
|
+
* Returns the lock handle that must be used in subsequent update/delete requests.
|
|
32
|
+
*
|
|
33
|
+
* NOTE: Caller must enable stateful session via connection.setSessionType('stateful') first.
|
|
34
|
+
*/
|
|
35
|
+
async function lockMessageClass(connection, name) {
|
|
36
|
+
const encoded = (0, internalUtils_1.encodeSapObjectName)(name.toLowerCase());
|
|
37
|
+
const url = `${BASE}/${encoded}?_action=LOCK&accessMode=MODIFY`;
|
|
38
|
+
const response = await connection.makeAdtRequest({
|
|
39
|
+
url,
|
|
40
|
+
method: 'POST',
|
|
41
|
+
timeout: (0, timeouts_1.getTimeout)('default'),
|
|
42
|
+
data: null,
|
|
43
|
+
headers: { Accept: contentTypes_1.ACCEPT_LOCK },
|
|
44
|
+
});
|
|
45
|
+
return parseLockHandle(response.data, 'message class lock response');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Lock an individual message for modification.
|
|
49
|
+
* Returns the message lock handle (MH) used in PUT XML as mc:lockhandle.
|
|
50
|
+
*
|
|
51
|
+
* NOTE: Caller must enable stateful session via connection.setSessionType('stateful') first.
|
|
52
|
+
*/
|
|
53
|
+
async function lockMessage(connection, name, no) {
|
|
54
|
+
const encoded = (0, internalUtils_1.encodeSapObjectName)(name.toLowerCase());
|
|
55
|
+
const url = `${BASE}/${encoded}/messages/${encodeURIComponent(no)}?_action=LOCK_MSG&accessMode=MODIFY`;
|
|
56
|
+
const response = await connection.makeAdtRequest({
|
|
57
|
+
url,
|
|
58
|
+
method: 'POST',
|
|
59
|
+
timeout: (0, timeouts_1.getTimeout)('default'),
|
|
60
|
+
data: null,
|
|
61
|
+
headers: { Accept: ACCEPT_LOCK_MSG },
|
|
62
|
+
});
|
|
63
|
+
return parseLockHandle(response.data, 'message lock response');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Lock a message class in the context of a specific message save.
|
|
67
|
+
* Returns the class lock handle (CH) used in PUT ?lockHandle= parameter.
|
|
68
|
+
*
|
|
69
|
+
* NOTE: Caller must enable stateful session via connection.setSessionType('stateful') first.
|
|
70
|
+
*/
|
|
71
|
+
async function lockClassForMessage(connection, name, no) {
|
|
72
|
+
const encoded = (0, internalUtils_1.encodeSapObjectName)(name.toLowerCase());
|
|
73
|
+
const url = `${BASE}/${encoded}?_action=LOCK&accessMode=MODIFY&msgNo=${encodeURIComponent(no)}&onSave=X`;
|
|
74
|
+
const response = await connection.makeAdtRequest({
|
|
75
|
+
url,
|
|
76
|
+
method: 'POST',
|
|
77
|
+
timeout: (0, timeouts_1.getTimeout)('default'),
|
|
78
|
+
data: null,
|
|
79
|
+
headers: { Accept: contentTypes_1.ACCEPT_LOCK },
|
|
80
|
+
});
|
|
81
|
+
return parseLockHandle(response.data, 'class-for-message lock response');
|
|
82
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message class read operations
|
|
3
|
+
*/
|
|
4
|
+
import type { IAbapConnection, IAdtResponse } from '@mcp-abap-adt/interfaces';
|
|
5
|
+
/**
|
|
6
|
+
* Read message class metadata and messages.
|
|
7
|
+
* GET /sap/bc/adt/messageclass/{name}
|
|
8
|
+
*/
|
|
9
|
+
export declare function getMessageClassSource(connection: IAbapConnection, name: string): Promise<IAdtResponse>;
|
|
10
|
+
//# sourceMappingURL=read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/messageClass/read.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAU9E;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAQvB"}
|