@mcp-abap-adt/adt-clients 3.9.0 → 3.9.2

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.
@@ -1,5 +1,8 @@
1
1
  /**
2
2
  * DataElement update operations
3
+ *
4
+ * Uses read-modify-write pattern: GET current XML → patch fields → PUT.
5
+ * This preserves all SAP-managed fields that would be lost if XML were built from scratch.
3
6
  */
4
7
  import type { IAdtResponse as AxiosResponse, IAbapConnection, ILogger } from '@mcp-abap-adt/interfaces';
5
8
  import type { IUpdateDataElementParams } from './types';
@@ -12,18 +15,17 @@ export declare function getDomainInfo(connection: IAbapConnection, domainName: s
12
15
  decimals: number;
13
16
  }>;
14
17
  /**
15
- * Update data element with new data
16
- * Requires object to be locked first (lockHandle must be provided)
18
+ * Update data element - atomic PUT operation (read-modify-write pattern)
19
+ * NOTE: Requires object to be locked first via lockDataElement()
20
+ * NOTE: Caller should call connection.setSessionType("stateful") before locking
21
+ */
22
+ export declare function updateDataElement(connection: IAbapConnection, params: IUpdateDataElementParams, lockHandle: string, logger?: ILogger): Promise<AxiosResponse>;
23
+ /**
24
+ * @deprecated Use updateDataElement directly. Kept for backward compatibility.
17
25
  */
18
- export declare function updateDataElementInternal(connection: IAbapConnection, args: IUpdateDataElementParams, lockHandle: string, username: string, _domainInfo: {
26
+ export declare function updateDataElementInternal(connection: IAbapConnection, args: IUpdateDataElementParams, lockHandle: string, _username: string, _domainInfo: {
19
27
  dataType: string;
20
28
  length: number;
21
29
  decimals: number;
22
30
  }, logger?: ILogger): Promise<AxiosResponse>;
23
- /**
24
- * Update data element - atomic PUT operation
25
- * NOTE: Requires object to be locked first via lockDataElement()
26
- * NOTE: Caller should call connection.setSessionType("stateful") before locking
27
- */
28
- export declare function updateDataElement(connection: IAbapConnection, params: IUpdateDataElementParams, lockHandle: string, logger?: ILogger): Promise<AxiosResponse>;
29
31
  //# sourceMappingURL=update.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/core/dataElement/update.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EACf,OAAO,EACR,MAAM,0BAA0B,CAAC;AAYlC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAIxD;;GAEG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,eAAe,EAC3B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAkCjE;AAkCD;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,wBAAwB,EAC9B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EACnE,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,aAAa,CAAC,CA+LxB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAE,wBAAwB,EAChC,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,aAAa,CAAC,CAgCxB"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/core/dataElement/update.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EACf,OAAO,EACR,MAAM,0BAA0B,CAAC;AAgBlC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAIxD;;GAEG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,eAAe,EAC3B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmCjE;AAqID;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAE,wBAAwB,EAChC,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,aAAa,CAAC,CAkExB;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,wBAAwB,EAC9B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EACnE,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,aAAa,CAAC,CAExB"}
@@ -1,21 +1,57 @@
1
1
  "use strict";
2
2
  /**
3
3
  * DataElement update operations
4
+ *
5
+ * Uses read-modify-write pattern: GET current XML → patch fields → PUT.
6
+ * This preserves all SAP-managed fields that would be lost if XML were built from scratch.
4
7
  */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
5
41
  Object.defineProperty(exports, "__esModule", { value: true });
6
42
  exports.getDomainInfo = getDomainInfo;
7
- exports.updateDataElementInternal = updateDataElementInternal;
8
43
  exports.updateDataElement = updateDataElement;
9
- const fast_xml_parser_1 = require("fast-xml-parser");
44
+ exports.updateDataElementInternal = updateDataElementInternal;
10
45
  const contentTypes_1 = require("../../constants/contentTypes");
11
46
  const internalUtils_1 = require("../../utils/internalUtils");
12
- const systemInfo_1 = require("../../utils/systemInfo");
13
47
  const timeouts_1 = require("../../utils/timeouts");
48
+ const xmlPatch_1 = require("../../utils/xmlPatch");
14
49
  const debugEnabled = process.env.DEBUG_ADT_LIBS === 'true';
15
50
  /**
16
51
  * Get domain info to extract dataType, length, decimals
17
52
  */
18
53
  async function getDomainInfo(connection, domainName) {
54
+ const { XMLParser } = await Promise.resolve().then(() => __importStar(require('fast-xml-parser')));
19
55
  const domainNameEncoded = (0, internalUtils_1.encodeSapObjectName)(domainName.toLowerCase());
20
56
  const url = `/sap/bc/adt/ddic/domains/${domainNameEncoded}`;
21
57
  const headers = {
@@ -27,7 +63,7 @@ async function getDomainInfo(connection, domainName) {
27
63
  timeout: (0, timeouts_1.getTimeout)('default'),
28
64
  headers,
29
65
  });
30
- const parser = new fast_xml_parser_1.XMLParser({
66
+ const parser = new XMLParser({
31
67
  ignoreAttributes: false,
32
68
  attributeNamePrefix: '',
33
69
  });
@@ -43,160 +79,106 @@ async function getDomainInfo(connection, domainName) {
43
79
  };
44
80
  }
45
81
  /**
46
- * Get data element to verify update
82
+ * Patch current data element XML with updated values.
83
+ * Only modifies fields that are explicitly provided in args.
47
84
  */
48
- async function _getDataElementForVerification(connection, dataElementName) {
49
- const dataElementNameEncoded = (0, internalUtils_1.encodeSapObjectName)(dataElementName.toLowerCase());
50
- const url = `/sap/bc/adt/ddic/dataelements/${dataElementNameEncoded}`;
51
- const headers = {
52
- Accept: contentTypes_1.ACCEPT_DATA_ELEMENT,
53
- };
54
- const response = await connection.makeAdtRequest({
55
- url,
56
- method: 'GET',
57
- timeout: (0, timeouts_1.getTimeout)('default'),
58
- headers,
59
- });
60
- const parser = new fast_xml_parser_1.XMLParser({
61
- ignoreAttributes: false,
62
- attributeNamePrefix: '',
63
- });
64
- const result = parser.parse(response.data);
65
- return result['blue:wbobj'];
66
- }
67
- /**
68
- * Update data element with new data
69
- * Requires object to be locked first (lockHandle must be provided)
70
- */
71
- async function updateDataElementInternal(connection, args, lockHandle, username, _domainInfo, logger) {
72
- const dataElementNameEncoded = (0, internalUtils_1.encodeSapObjectName)(args.data_element_name.toLowerCase());
73
- const corrNrParam = args.transport_request
74
- ? `&corrNr=${args.transport_request}`
75
- : '';
76
- const url = `/sap/bc/adt/ddic/dataelements/${dataElementNameEncoded}?lockHandle=${encodeURIComponent(lockHandle)}${corrNrParam}`;
77
- if (!args.type_kind) {
78
- throw new Error('type_kind is required. Must be one of: domain, predefinedAbapType, refToPredefinedAbapType, refToDictionaryType, refToClifType');
79
- }
80
- // Validate required parameters based on type_kind
81
- // predefinedAbapType and refToPredefinedAbapType require data_type
82
- // Other types (domain, refToDictionaryType, refToClifType) require type_name
83
- if (args.type_kind === 'predefinedAbapType' ||
84
- args.type_kind === 'refToPredefinedAbapType') {
85
- if (!args.data_type) {
86
- throw new Error(`data_type is required when type_kind is '${args.type_kind}'. Provide data type (e.g., CHAR, NUMC, INT4).`);
87
- }
85
+ function patchDataElementXml(currentXml, args) {
86
+ let xml = currentXml;
87
+ // Description
88
+ if (args.description) {
89
+ const description = (0, internalUtils_1.limitDescription)(args.description);
90
+ xml = (0, xmlPatch_1.patchXmlAttribute)(xml, 'adtcore:description', description);
88
91
  }
89
- else {
90
- // domain, refToDictionaryType, refToClifType require type_name
92
+ // Type information
93
+ xml = (0, xmlPatch_1.patchIf)(xml, args.type_kind, (x, val) => (0, xmlPatch_1.patchXmlElement)(x, 'dtel:typeKind', val));
94
+ // typeName — handle domain type: use type_name or data_type as domain name
95
+ if (args.type_kind || args.type_name || args.data_type) {
96
+ let typeName = '';
91
97
  if (args.type_kind === 'domain') {
92
- // For domain, type_name (domain name) is required, but it will be used as data_type internally
93
- if (!args.type_name && !args.data_type) {
94
- throw new Error(`type_name (domain name) is required when type_kind is 'domain'. Provide domain name (e.g., ZOK_AUTH_ID).`);
95
- }
98
+ typeName = (args.type_name || args.data_type || '').toUpperCase();
96
99
  }
97
- else {
98
- // refToDictionaryType, refToClifType
99
- if (!args.type_name) {
100
- throw new Error(`type_name is required when type_kind is '${args.type_kind}'. Provide ${args.type_kind === 'refToDictionaryType' ? 'data element name' : 'class name'}.`);
101
- }
100
+ else if (args.type_name) {
101
+ typeName = args.type_name.toUpperCase();
102
+ }
103
+ if (typeName) {
104
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:typeName', typeName);
102
105
  }
103
106
  }
104
- const typeKind = args.type_kind;
105
- // Use provided values directly - no automatic determination
106
- // When typeKind is 'domain', dataType should contain the domain name, and it goes to typeName in XML
107
- let typeName = '';
108
- if (typeKind === 'domain') {
109
- // For domain type, typeName is the domain name (type_name), not the underlying data type
110
- typeName = (args.type_name || args.data_type || '').toUpperCase();
111
- }
112
- else {
113
- // For other types, typeName comes from type_name parameter
114
- typeName = args.type_name ? args.type_name.toUpperCase() : '';
115
- }
116
- // Use provided values directly
117
- const dataType = args.data_type || '';
118
- const dataTypeLength = args.length || 0;
119
- const dataTypeDecimals = args.decimals || 0;
120
- const shortMaxLength = 10;
121
- const mediumMaxLength = 20;
122
- const longMaxLength = 40;
123
- const headingMaxLength = 55;
124
- const shortLabel = (args.short_label || '').substring(0, shortMaxLength);
125
- const mediumLabel = (args.medium_label || '').substring(0, mediumMaxLength);
126
- const longLabel = (args.long_label || '').substring(0, longMaxLength);
127
- const headingLabel = (args.heading_label || '').substring(0, headingMaxLength);
128
- const shortLength = shortLabel.length || shortMaxLength;
129
- const mediumLength = mediumLabel.length || mediumMaxLength;
130
- const longLength = longLabel.length || longMaxLength;
131
- const headingLength = headingLabel.length || headingMaxLength;
132
- const searchHelp = args.search_help !== undefined ? args.search_help : '';
133
- const searchHelpParameter = args.search_help_parameter !== undefined ? args.search_help_parameter : '';
134
- const setGetParameter = args.set_get_parameter !== undefined ? args.set_get_parameter : '';
135
- const defaultComponentName = args.default_component_name !== undefined
136
- ? args.default_component_name
137
- : '';
138
- const deactivateInputHistory = args.deactivate_input_history !== undefined
139
- ? args.deactivate_input_history
140
- : false;
141
- const changeDocument = args.change_document !== undefined ? args.change_document : false;
142
- const leftToRightDirection = args.left_to_right_direction !== undefined
143
- ? args.left_to_right_direction
144
- : false;
145
- const deactivateBIDIFiltering = args.deactivate_bidi_filtering !== undefined
146
- ? args.deactivate_bidi_filtering
147
- : false;
148
- // Description is limited to 60 characters in SAP ADT
149
- const description = (0, internalUtils_1.limitDescription)(args.description || args.data_element_name);
150
- const responsibleAttr = username ? ` adtcore:responsible="${username}"` : '';
151
- const xmlBody = `<?xml version="1.0" encoding="UTF-8"?>
152
- <blue:wbobj xmlns:blue="http://www.sap.com/wbobj/dictionary/dtel"
153
- xmlns:adtcore="http://www.sap.com/adt/core"
154
- xmlns:atom="http://www.w3.org/2005/Atom"
155
- xmlns:dtel="http://www.sap.com/adt/dictionary/dataelements"
156
- adtcore:description="${description}"
157
- adtcore:language="EN"
158
- adtcore:name="${args.data_element_name.toUpperCase()}"
159
- adtcore:type="DTEL/DE"
160
- adtcore:masterLanguage="EN"${responsibleAttr}>
161
- <adtcore:packageRef adtcore:name="${args.package_name.toUpperCase()}"/>
162
- <dtel:dataElement>
163
- <dtel:typeKind>${typeKind}</dtel:typeKind>
164
- ${typeName ? `<dtel:typeName>${typeName}</dtel:typeName>` : '<dtel:typeName/>'}
165
- ${dataType ? `<dtel:dataType>${dataType}</dtel:dataType>` : '<dtel:dataType/>'}
166
- <dtel:dataTypeLength>${String(dataTypeLength).padStart(6, '0')}</dtel:dataTypeLength>
167
- <dtel:dataTypeDecimals>${String(dataTypeDecimals).padStart(6, '0')}</dtel:dataTypeDecimals>
168
- <dtel:shortFieldLabel>${shortLabel}</dtel:shortFieldLabel>
169
- <dtel:shortFieldLength>${shortLength}</dtel:shortFieldLength>
170
- <dtel:shortFieldMaxLength>${shortMaxLength}</dtel:shortFieldMaxLength>
171
- <dtel:mediumFieldLabel>${mediumLabel}</dtel:mediumFieldLabel>
172
- <dtel:mediumFieldLength>${mediumLength}</dtel:mediumFieldLength>
173
- <dtel:mediumFieldMaxLength>${mediumMaxLength}</dtel:mediumFieldMaxLength>
174
- <dtel:longFieldLabel>${longLabel}</dtel:longFieldLabel>
175
- <dtel:longFieldLength>${longLength}</dtel:longFieldLength>
176
- <dtel:longFieldMaxLength>${longMaxLength}</dtel:longFieldMaxLength>
177
- <dtel:headingFieldLabel>${headingLabel}</dtel:headingFieldLabel>
178
- <dtel:headingFieldLength>${headingLength}</dtel:headingFieldLength>
179
- <dtel:headingFieldMaxLength>${headingMaxLength}</dtel:headingFieldMaxLength>
180
- ${searchHelp ? `<dtel:searchHelp>${searchHelp}</dtel:searchHelp>` : '<dtel:searchHelp/>'}
181
- ${searchHelpParameter ? `<dtel:searchHelpParameter>${searchHelpParameter}</dtel:searchHelpParameter>` : '<dtel:searchHelpParameter/>'}
182
- ${setGetParameter ? `<dtel:setGetParameter>${setGetParameter}</dtel:setGetParameter>` : '<dtel:setGetParameter/>'}
183
- ${defaultComponentName ? `<dtel:defaultComponentName>${defaultComponentName}</dtel:defaultComponentName>` : '<dtel:defaultComponentName/>'}
184
- <dtel:deactivateInputHistory>${deactivateInputHistory}</dtel:deactivateInputHistory>
185
- <dtel:changeDocument>${changeDocument}</dtel:changeDocument>
186
- <dtel:leftToRightDirection>${leftToRightDirection}</dtel:leftToRightDirection>
187
- <dtel:deactivateBIDIFiltering>${deactivateBIDIFiltering}</dtel:deactivateBIDIFiltering>
188
- </dtel:dataElement>
189
- </blue:wbobj>`;
190
- const headers = {
191
- Accept: contentTypes_1.ACCEPT_DATA_ELEMENT,
192
- 'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8',
193
- };
194
- // Debug: log XML when DEBUG_ADT_LIBS is enabled (formatted for readability)
107
+ xml = (0, xmlPatch_1.patchIf)(xml, args.data_type, (x, val) => (0, xmlPatch_1.patchXmlElement)(x, 'dtel:dataType', val));
108
+ xml = (0, xmlPatch_1.patchIf)(xml, args.length, (x, val) => (0, xmlPatch_1.patchXmlElement)(x, 'dtel:dataTypeLength', String(val).padStart(6, '0')));
109
+ xml = (0, xmlPatch_1.patchIf)(xml, args.decimals, (x, val) => (0, xmlPatch_1.patchXmlElement)(x, 'dtel:dataTypeDecimals', String(val).padStart(6, '0')));
110
+ // Labels
111
+ if (args.short_label !== undefined) {
112
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:shortFieldLabel', args.short_label);
113
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:shortFieldLength', String(args.short_label.length || 10));
114
+ }
115
+ if (args.medium_label !== undefined) {
116
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:mediumFieldLabel', args.medium_label);
117
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:mediumFieldLength', String(args.medium_label.length || 20));
118
+ }
119
+ if (args.long_label !== undefined) {
120
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:longFieldLabel', args.long_label);
121
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:longFieldLength', String(args.long_label.length || 40));
122
+ }
123
+ if (args.heading_label !== undefined) {
124
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:headingFieldLabel', args.heading_label);
125
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:headingFieldLength', String(args.heading_label.length || 55));
126
+ }
127
+ // Optional fields
128
+ if (args.search_help !== undefined) {
129
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:searchHelp', args.search_help);
130
+ }
131
+ if (args.search_help_parameter !== undefined) {
132
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:searchHelpParameter', args.search_help_parameter);
133
+ }
134
+ if (args.set_get_parameter !== undefined) {
135
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:setGetParameter', args.set_get_parameter);
136
+ }
137
+ if (args.default_component_name !== undefined) {
138
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:defaultComponentName', args.default_component_name);
139
+ }
140
+ if (args.deactivate_input_history !== undefined) {
141
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:deactivateInputHistory', String(args.deactivate_input_history));
142
+ }
143
+ if (args.change_document !== undefined) {
144
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:changeDocument', String(args.change_document));
145
+ }
146
+ if (args.left_to_right_direction !== undefined) {
147
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:leftToRightDirection', String(args.left_to_right_direction));
148
+ }
149
+ if (args.deactivate_bidi_filtering !== undefined) {
150
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'dtel:deactivateBIDIFiltering', String(args.deactivate_bidi_filtering));
151
+ }
152
+ return xml;
153
+ }
154
+ /**
155
+ * Update data element - atomic PUT operation (read-modify-write pattern)
156
+ * NOTE: Requires object to be locked first via lockDataElement()
157
+ * NOTE: Caller should call connection.setSessionType("stateful") before locking
158
+ */
159
+ async function updateDataElement(connection, params, lockHandle, logger) {
160
+ if (!params.data_element_name) {
161
+ throw new Error('Data element name is required');
162
+ }
163
+ if (!params.package_name) {
164
+ throw new Error('Package name is required');
165
+ }
166
+ const dataElementNameEncoded = (0, internalUtils_1.encodeSapObjectName)(params.data_element_name.toLowerCase());
167
+ // 1. GET current XML
168
+ const currentResponse = await connection.makeAdtRequest({
169
+ url: `/sap/bc/adt/ddic/dataelements/${dataElementNameEncoded}`,
170
+ method: 'GET',
171
+ timeout: (0, timeouts_1.getTimeout)('default'),
172
+ headers: { Accept: contentTypes_1.ACCEPT_DATA_ELEMENT },
173
+ });
174
+ const currentXml = (0, xmlPatch_1.extractXmlString)(currentResponse.data);
175
+ // 2. Patch only changed fields
176
+ const updatedXml = patchDataElementXml(currentXml, params);
177
+ // Debug: log XML when DEBUG_ADT_LIBS is enabled
195
178
  if (debugEnabled) {
196
179
  logger?.debug?.('[UPDATE XML]');
197
- // Format XML with indentation for readability
198
180
  try {
199
- const { XMLParser, XMLBuilder } = require('fast-xml-parser');
181
+ const { XMLParser, XMLBuilder } = await Promise.resolve().then(() => __importStar(require('fast-xml-parser')));
200
182
  const parser = new XMLParser({
201
183
  ignoreAttributes: false,
202
184
  attributeNamePrefix: '',
@@ -207,46 +189,34 @@ async function updateDataElementInternal(connection, args, lockHandle, username,
207
189
  format: true,
208
190
  indentBy: ' ',
209
191
  });
210
- const parsed = parser.parse(xmlBody);
192
+ const parsed = parser.parse(updatedXml);
211
193
  const formatted = builder.build(parsed);
212
194
  logger?.debug?.(formatted);
213
195
  }
214
196
  catch {
215
- // If formatting fails, just log as-is
216
- logger?.debug?.(xmlBody);
197
+ logger?.debug?.(updatedXml);
217
198
  }
218
199
  }
200
+ // 3. PUT
201
+ const corrNrParam = params.transport_request
202
+ ? `&corrNr=${params.transport_request}`
203
+ : '';
204
+ const url = `/sap/bc/adt/ddic/dataelements/${dataElementNameEncoded}?lockHandle=${encodeURIComponent(lockHandle)}${corrNrParam}`;
205
+ const headers = {
206
+ Accept: contentTypes_1.ACCEPT_DATA_ELEMENT,
207
+ 'Content-Type': 'application/vnd.sap.adt.dataelements.v2+xml; charset=utf-8',
208
+ };
219
209
  return connection.makeAdtRequest({
220
210
  url,
221
211
  method: 'PUT',
222
212
  timeout: (0, timeouts_1.getTimeout)('default'),
223
- data: xmlBody,
213
+ data: updatedXml,
224
214
  headers,
225
215
  });
226
216
  }
227
217
  /**
228
- * Update data element - atomic PUT operation
229
- * NOTE: Requires object to be locked first via lockDataElement()
230
- * NOTE: Caller should call connection.setSessionType("stateful") before locking
218
+ * @deprecated Use updateDataElement directly. Kept for backward compatibility.
231
219
  */
232
- async function updateDataElement(connection, params, lockHandle, logger) {
233
- if (!params.data_element_name) {
234
- throw new Error('Data element name is required');
235
- }
236
- if (!params.package_name) {
237
- throw new Error('Package name is required');
238
- }
239
- if (!params.type_kind) {
240
- throw new Error('type_kind is required. Must be one of: domain, predefinedAbapType, refToPredefinedAbapType, refToDictionaryType, refToClifType');
241
- }
242
- // Get system information for username
243
- const systemInfo = await (0, systemInfo_1.getSystemInformation)(connection);
244
- const username = systemInfo?.userName || '';
245
- // Use provided values directly - no automatic determination
246
- const domainInfo = {
247
- dataType: params.data_type || '',
248
- length: params.length || 0,
249
- decimals: params.decimals || 0,
250
- };
251
- return updateDataElementInternal(connection, params, lockHandle, username, domainInfo, logger);
220
+ async function updateDataElementInternal(connection, args, lockHandle, _username, _domainInfo, logger) {
221
+ return updateDataElement(connection, args, lockHandle, logger);
252
222
  }
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * Domain update operations
3
+ *
4
+ * Uses read-modify-write pattern: GET current XML → patch fields → PUT.
5
+ * This preserves all SAP-managed fields that would be lost if XML were built from scratch.
3
6
  */
4
7
  import type { IAdtResponse as AxiosResponse, IAbapConnection } from '@mcp-abap-adt/interfaces';
5
8
  import type { IUpdateDomainParams } from './types';
6
9
  /**
7
- * Update domain with new data
10
+ * Update domain with new data (read-modify-write pattern)
8
11
  *
9
12
  * NOTE: Requires stateful session mode enabled via connection.setSessionType("stateful")
10
13
  */
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/core/domain/update.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAOlC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,mBAAmB,EACzB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CA0ExB"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/core/domain/update.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAelC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAsEnD;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,eAAe,EAC3B,IAAI,EAAE,mBAAmB,EACzB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAiCxB"}
@@ -1,72 +1,82 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Domain update operations
4
+ *
5
+ * Uses read-modify-write pattern: GET current XML → patch fields → PUT.
6
+ * This preserves all SAP-managed fields that would be lost if XML were built from scratch.
4
7
  */
5
8
  Object.defineProperty(exports, "__esModule", { value: true });
6
9
  exports.updateDomain = updateDomain;
7
10
  const contentTypes_1 = require("../../constants/contentTypes");
8
11
  const internalUtils_1 = require("../../utils/internalUtils");
9
12
  const timeouts_1 = require("../../utils/timeouts");
13
+ const xmlPatch_1 = require("../../utils/xmlPatch");
14
+ /**
15
+ * Patch current domain XML with updated values.
16
+ * Only modifies fields that are explicitly provided in args.
17
+ */
18
+ function patchDomainXml(currentXml, args) {
19
+ let xml = currentXml;
20
+ // Description
21
+ if (args.description) {
22
+ const description = (0, internalUtils_1.limitDescription)(args.description);
23
+ xml = (0, xmlPatch_1.patchXmlAttribute)(xml, 'adtcore:description', description);
24
+ }
25
+ // Type information
26
+ xml = (0, xmlPatch_1.patchIf)(xml, args.datatype, (x, val) => (0, xmlPatch_1.patchXmlElement)(x, 'doma:datatype', val));
27
+ xml = (0, xmlPatch_1.patchIf)(xml, args.length, (x, val) => (0, xmlPatch_1.patchXmlElement)(x, 'doma:length', String(val)));
28
+ xml = (0, xmlPatch_1.patchIf)(xml, args.decimals, (x, val) => (0, xmlPatch_1.patchXmlElement)(x, 'doma:decimals', String(val)));
29
+ // Output information
30
+ if (args.conversion_exit !== undefined) {
31
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'doma:conversionExit', args.conversion_exit || '');
32
+ }
33
+ if (args.sign_exists !== undefined) {
34
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'doma:signExists', String(args.sign_exists));
35
+ }
36
+ if (args.lowercase !== undefined) {
37
+ xml = (0, xmlPatch_1.patchXmlElement)(xml, 'doma:lowercase', String(args.lowercase));
38
+ }
39
+ // Value table
40
+ if (args.value_table !== undefined) {
41
+ xml = (0, xmlPatch_1.patchXmlElementAttribute)(xml, 'doma:valueTableRef', 'adtcore:name', args.value_table || '');
42
+ }
43
+ // Fixed values — replace entire block
44
+ if (args.fixed_values !== undefined) {
45
+ if (args.fixed_values && args.fixed_values.length > 0) {
46
+ const fixValueItems = args.fixed_values
47
+ .map((fv) => ` <doma:fixValue>\n <doma:low>${fv.low}</doma:low>\n <doma:text>${fv.text}</doma:text>\n </doma:fixValue>`)
48
+ .join('\n');
49
+ const fixValuesBlock = `<doma:fixValues>\n${fixValueItems}\n </doma:fixValues>`;
50
+ xml = (0, xmlPatch_1.patchXmlBlock)(xml, 'doma:fixValues', fixValuesBlock);
51
+ }
52
+ else {
53
+ xml = (0, xmlPatch_1.patchXmlBlock)(xml, 'doma:fixValues', '<doma:fixValues/>');
54
+ }
55
+ }
56
+ return xml;
57
+ }
10
58
  /**
11
- * Update domain with new data
59
+ * Update domain with new data (read-modify-write pattern)
12
60
  *
13
61
  * NOTE: Requires stateful session mode enabled via connection.setSessionType("stateful")
14
62
  */
15
63
  async function updateDomain(connection, args, lockHandle) {
16
64
  const domainNameEncoded = (0, internalUtils_1.encodeSapObjectName)(args.domain_name.toLowerCase());
65
+ // 1. GET current XML
66
+ const currentResponse = await connection.makeAdtRequest({
67
+ url: `/sap/bc/adt/ddic/domains/${domainNameEncoded}`,
68
+ method: 'GET',
69
+ timeout: (0, timeouts_1.getTimeout)('default'),
70
+ headers: { Accept: contentTypes_1.ACCEPT_DOMAIN },
71
+ });
72
+ const currentXml = (0, xmlPatch_1.extractXmlString)(currentResponse.data);
73
+ // 2. Patch only changed fields
74
+ const updatedXml = patchDomainXml(currentXml, args);
75
+ // 3. PUT
17
76
  const corrNrParam = args.transport_request
18
77
  ? `&corrNr=${args.transport_request}`
19
78
  : '';
20
79
  const url = `/sap/bc/adt/ddic/domains/${domainNameEncoded}?lockHandle=${encodeURIComponent(lockHandle)}${corrNrParam}`;
21
- const datatype = args.datatype || 'CHAR';
22
- const length = args.length || 100;
23
- const decimals = args.decimals || 0;
24
- let fixValuesXml = '';
25
- if (args.fixed_values && args.fixed_values.length > 0) {
26
- const fixValueItems = args.fixed_values
27
- .map((fv) => ` <doma:fixValue>\n <doma:low>${fv.low}</doma:low>\n <doma:text>${fv.text}</doma:text>\n </doma:fixValue>`)
28
- .join('\n');
29
- fixValuesXml = ` <doma:fixValues>\n${fixValueItems}\n </doma:fixValues>`;
30
- }
31
- else {
32
- fixValuesXml = ' <doma:fixValues/>';
33
- }
34
- // Description is limited to 60 characters in SAP ADT
35
- const description = (0, internalUtils_1.limitDescription)(args.description || args.domain_name);
36
- const masterSystemAttr = args.masterSystem
37
- ? ` adtcore:masterSystem="${args.masterSystem}"`
38
- : '';
39
- const responsibleAttr = args.responsible
40
- ? ` adtcore:responsible="${args.responsible}"`
41
- : '';
42
- const xmlBody = `<?xml version="1.0" encoding="UTF-8"?>
43
- <doma:domain xmlns:doma="http://www.sap.com/dictionary/domain"
44
- xmlns:adtcore="http://www.sap.com/adt/core"
45
- adtcore:description="${description}"
46
- adtcore:language="EN"
47
- adtcore:name="${args.domain_name.toUpperCase()}"
48
- adtcore:type="DOMA/DD"
49
- adtcore:masterLanguage="EN"${masterSystemAttr}${responsibleAttr}>
50
- <adtcore:packageRef adtcore:name="${args.package_name.toUpperCase()}"/>
51
- <doma:content>
52
- <doma:typeInformation>
53
- <doma:datatype>${datatype}</doma:datatype>
54
- <doma:length>${length}</doma:length>
55
- <doma:decimals>${decimals}</doma:decimals>
56
- </doma:typeInformation>
57
- <doma:outputInformation>
58
- <doma:length>${length}</doma:length>
59
- <doma:conversionExit>${args.conversion_exit || ''}</doma:conversionExit>
60
- <doma:signExists>${args.sign_exists || false}</doma:signExists>
61
- <doma:lowercase>${args.lowercase || false}</doma:lowercase>
62
- </doma:outputInformation>
63
- <doma:valueInformation>
64
- <doma:valueTableRef adtcore:name="${args.value_table || ''}"/>
65
- <doma:appendExists>false</doma:appendExists>
66
- ${fixValuesXml}
67
- </doma:valueInformation>
68
- </doma:content>
69
- </doma:domain>`;
70
80
  const headers = {
71
81
  Accept: contentTypes_1.ACCEPT_DOMAIN,
72
82
  'Content-Type': 'application/vnd.sap.adt.domains.v2+xml; charset=utf-8',
@@ -75,7 +85,7 @@ ${fixValuesXml}
75
85
  url,
76
86
  method: 'PUT',
77
87
  timeout: (0, timeouts_1.getTimeout)('default'),
78
- data: xmlBody,
88
+ data: updatedXml,
79
89
  headers,
80
90
  });
81
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/core/functionGroup/update.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAE7B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAQlC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AA2D1D;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAE,0BAA0B,EAClC,YAAY,CAAC,EAAE,gBAAgB,GAC9B,OAAO,CAAC,aAAa,CAAC,CAkGxB"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/core/functionGroup/update.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAE7B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAQlC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AA2C1D;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAE,0BAA0B,EAClC,YAAY,CAAC,EAAE,gBAAgB,GAC9B,OAAO,CAAC,aAAa,CAAC,CAmGxB"}