@mcp-abap-adt/adt-backup 1.1.0 → 1.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/README.md +6 -0
- package/dist/lib/backup/readBasicMetadata.d.ts.map +1 -1
- package/dist/lib/backup/readBasicMetadata.js +7 -0
- package/dist/lib/backup/readMetadataXmlForType.d.ts.map +1 -1
- package/dist/lib/backup/readMetadataXmlForType.js +37 -17
- package/dist/lib/backup/readSourceText.d.ts.map +1 -1
- package/dist/lib/backup/readSourceText.js +6 -0
- package/dist/lib/constants/typeOrder.d.ts.map +1 -1
- package/dist/lib/constants/typeOrder.js +1 -0
- package/dist/lib/dependencies/collectTreeDependencies.d.ts.map +1 -1
- package/dist/lib/dependencies/collectTreeDependencies.js +1 -0
- package/dist/lib/restore/analyzeDependencies.d.ts +6 -0
- package/dist/lib/restore/analyzeDependencies.d.ts.map +1 -1
- package/dist/lib/restore/analyzeDependencies.js +127 -28
- package/dist/lib/restore/restoreObject.d.ts.map +1 -1
- package/dist/lib/restore/restoreObject.js +20 -0
- package/dist/lib/restore/restoreObjects.d.ts.map +1 -1
- package/dist/lib/restore/restoreObjects.js +1 -0
- package/dist/lib/restore/restoreTreeBackup.d.ts +2 -2
- package/dist/lib/restore/restoreTreeBackup.d.ts.map +1 -1
- package/dist/lib/restore/restoreTreeBackup.js +229 -97
- package/dist/lib/restore/restoreTreeNode.d.ts.map +1 -1
- package/dist/lib/restore/restoreTreeNode.js +17 -0
- package/dist/lib/run.d.ts.map +1 -1
- package/dist/lib/run.js +103 -44
- package/dist/lib/tree/isRestoreImplemented.d.ts.map +1 -1
- package/dist/lib/tree/isRestoreImplemented.js +1 -0
- package/dist/lib/tree/mapAdtTypeToSupported.d.ts.map +1 -1
- package/dist/lib/tree/mapAdtTypeToSupported.js +4 -0
- package/dist/lib/tree/readPayloadForType.d.ts.map +1 -1
- package/dist/lib/tree/readPayloadForType.js +1 -0
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/utils/applyConfigName.d.ts.map +1 -1
- package/dist/lib/utils/applyConfigName.js +3 -0
- package/dist/lib/utils/detectTransformationType.d.ts +3 -0
- package/dist/lib/utils/detectTransformationType.d.ts.map +1 -0
- package/dist/lib/utils/detectTransformationType.js +9 -0
- package/dist/lib/utils/normalizeType.d.ts.map +1 -1
- package/dist/lib/utils/normalizeType.js +4 -0
- package/dist/lib/verify/findOtherType.d.ts.map +1 -1
- package/dist/lib/verify/findOtherType.js +1 -0
- package/dist/lib/xml/parseServiceBindingConfig.d.ts.map +1 -1
- package/dist/lib/xml/parseServiceBindingConfig.js +24 -5
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# @mcp-abap-adt/adt-backup
|
|
2
|
+
[](https://stand-with-ukraine.pp.ua)
|
|
2
3
|
|
|
3
4
|
CLI for recursive ADT backups and restores using `@mcp-abap-adt/adt-clients`.
|
|
4
5
|
|
|
@@ -78,6 +79,7 @@ See `docs/roadmap.yaml` for per-object backup/restore status and the plan for re
|
|
|
78
79
|
| `interface` | implemented | implemented | source |
|
|
79
80
|
| `class` | implemented | implemented | source |
|
|
80
81
|
| `program` | implemented | implemented | source |
|
|
82
|
+
| `transformation` | implemented | implemented | source |
|
|
81
83
|
| `serviceDefinition` | implemented | implemented | source |
|
|
82
84
|
| `serviceBinding` | implemented | implemented | metadata-xml |
|
|
83
85
|
| `metadataExtension` | implemented | implemented | source |
|
|
@@ -89,6 +91,10 @@ See `docs/roadmap.yaml` for per-object backup/restore status and the plan for re
|
|
|
89
91
|
|
|
90
92
|
> **Note**: Unit tests are stored as classes in backups. When restoring, they are created as test classes in the system.
|
|
91
93
|
|
|
94
|
+
> **Note**: For `transformation`, the subtype (`SimpleTransformation` for `XSLT/VT` vs `XSLTProgram` for `XSLT/ST`) is detected from the source header (`<?sap.transform simple?>`).
|
|
95
|
+
|
|
96
|
+
> **Note**: For `serviceBinding`, the publication state (`srvb:published`) is preserved in the backup and re-applied during restore — published bindings are re-published, unpublished bindings are unpublished.
|
|
97
|
+
|
|
92
98
|
## Smoke Checklist
|
|
93
99
|
|
|
94
100
|
When your landscape is ready, use `docs/SMOKE_CHECKLIST.md` for a focused backup/restore/verify checklist.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readBasicMetadata.d.ts","sourceRoot":"","sources":["../../../src/lib/backup/readBasicMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAI3C,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"readBasicMetadata.d.ts","sourceRoot":"","sources":["../../../src/lib/backup/readBasicMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAI3C,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAiEzD"}
|
|
@@ -26,6 +26,13 @@ async function readBasicMetadata(client, spec) {
|
|
|
26
26
|
const xml = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
27
27
|
return xml ? (0, extractMetadata_1.extractMetadata)(xml) : {};
|
|
28
28
|
}
|
|
29
|
+
case 'transformation': {
|
|
30
|
+
const state = await client
|
|
31
|
+
.getTransformation()
|
|
32
|
+
.readMetadata({ transformationName: spec.name });
|
|
33
|
+
const xml = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
34
|
+
return xml ? (0, extractMetadata_1.extractMetadata)(xml) : {};
|
|
35
|
+
}
|
|
29
36
|
case 'structure': {
|
|
30
37
|
const state = await client
|
|
31
38
|
.getStructure()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readMetadataXmlForType.d.ts","sourceRoot":"","sources":["../../../src/lib/backup/readMetadataXmlForType.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE,MAAM,EACZ,kBAAkB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"readMetadataXmlForType.d.ts","sourceRoot":"","sources":["../../../src/lib/backup/readMetadataXmlForType.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE,MAAM,EACZ,kBAAkB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CA0IpC"}
|
|
@@ -4,106 +4,126 @@ exports.readMetadataXmlForType = readMetadataXmlForType;
|
|
|
4
4
|
const responseToText_1 = require("../utils/responseToText");
|
|
5
5
|
async function readMetadataXmlForType(client, type, name, _functionGroupName) {
|
|
6
6
|
try {
|
|
7
|
+
let result;
|
|
7
8
|
switch (type) {
|
|
8
9
|
case 'package': {
|
|
9
10
|
const state = await client
|
|
10
11
|
.getPackage()
|
|
11
12
|
.readMetadata({ packageName: name });
|
|
12
|
-
|
|
13
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
14
|
+
break;
|
|
13
15
|
}
|
|
14
16
|
case 'class': {
|
|
15
17
|
const state = await client.getClass().readMetadata({ className: name });
|
|
16
|
-
|
|
18
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
19
|
+
break;
|
|
17
20
|
}
|
|
18
21
|
case 'interface': {
|
|
19
22
|
const state = await client
|
|
20
23
|
.getInterface()
|
|
21
24
|
.readMetadata({ interfaceName: name });
|
|
22
|
-
|
|
25
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
26
|
+
break;
|
|
23
27
|
}
|
|
24
28
|
case 'domain': {
|
|
25
29
|
const state = await client
|
|
26
30
|
.getDomain()
|
|
27
31
|
.readMetadata({ domainName: name });
|
|
28
|
-
|
|
32
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
33
|
+
break;
|
|
29
34
|
}
|
|
30
35
|
case 'dataElement': {
|
|
31
36
|
const state = await client
|
|
32
37
|
.getDataElement()
|
|
33
38
|
.readMetadata({ dataElementName: name });
|
|
34
|
-
|
|
39
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
40
|
+
break;
|
|
35
41
|
}
|
|
36
42
|
case 'table': {
|
|
37
43
|
const state = await client.getTable().readMetadata({ tableName: name });
|
|
38
|
-
|
|
44
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
45
|
+
break;
|
|
39
46
|
}
|
|
40
47
|
case 'tableType': {
|
|
41
48
|
const state = await client
|
|
42
49
|
.getTableType()
|
|
43
50
|
.readMetadata({ tableTypeName: name });
|
|
44
|
-
|
|
51
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
52
|
+
break;
|
|
45
53
|
}
|
|
46
54
|
case 'structure': {
|
|
47
55
|
const state = await client
|
|
48
56
|
.getStructure()
|
|
49
57
|
.readMetadata({ structureName: name });
|
|
50
|
-
|
|
58
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
59
|
+
break;
|
|
51
60
|
}
|
|
52
61
|
case 'view': {
|
|
53
62
|
const state = await client.getView().readMetadata({ viewName: name });
|
|
54
|
-
|
|
63
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
64
|
+
break;
|
|
55
65
|
}
|
|
56
66
|
case 'behaviorDefinition': {
|
|
57
67
|
const state = await client
|
|
58
68
|
.getBehaviorDefinition()
|
|
59
69
|
.readMetadata({ name });
|
|
60
|
-
|
|
70
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
71
|
+
break;
|
|
61
72
|
}
|
|
62
73
|
case 'behaviorImplementation': {
|
|
63
74
|
const state = await client
|
|
64
75
|
.getBehaviorImplementation()
|
|
65
76
|
.readMetadata({ className: name });
|
|
66
|
-
|
|
77
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
78
|
+
break;
|
|
67
79
|
}
|
|
68
80
|
case 'serviceDefinition': {
|
|
69
81
|
const state = await client
|
|
70
82
|
.getServiceDefinition()
|
|
71
83
|
.readMetadata({ serviceDefinitionName: name });
|
|
72
|
-
|
|
84
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
85
|
+
break;
|
|
73
86
|
}
|
|
74
87
|
case 'serviceBinding': {
|
|
75
88
|
const state = await client
|
|
76
89
|
.getServiceBinding()
|
|
77
90
|
.readMetadata({ bindingName: name });
|
|
78
|
-
|
|
91
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
92
|
+
break;
|
|
79
93
|
}
|
|
80
94
|
case 'metadataExtension': {
|
|
81
95
|
const state = await client
|
|
82
96
|
.getMetadataExtension()
|
|
83
97
|
.readMetadata({ name });
|
|
84
|
-
|
|
98
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
99
|
+
break;
|
|
85
100
|
}
|
|
86
101
|
case 'functionGroup': {
|
|
87
102
|
const state = await client
|
|
88
103
|
.getFunctionGroup()
|
|
89
104
|
.readMetadata({ functionGroupName: name });
|
|
90
|
-
|
|
105
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
106
|
+
break;
|
|
91
107
|
}
|
|
92
108
|
case 'enhancement': {
|
|
93
109
|
const state = await client
|
|
94
110
|
.getEnhancement()
|
|
95
111
|
.readMetadata({ enhancementName: name, enhancementType: 'enhoxh' });
|
|
96
|
-
|
|
112
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
113
|
+
break;
|
|
97
114
|
}
|
|
98
115
|
case 'accessControl': {
|
|
99
116
|
const state = await client
|
|
100
117
|
.getAccessControl()
|
|
101
118
|
.readMetadata({ accessControlName: name });
|
|
102
|
-
|
|
119
|
+
result = (0, responseToText_1.responseToText)(state.metadataResult);
|
|
120
|
+
break;
|
|
103
121
|
}
|
|
104
122
|
default:
|
|
105
123
|
return undefined;
|
|
106
124
|
}
|
|
125
|
+
// ADT clients return undefined on 404 — treat as not found
|
|
126
|
+
return result ?? null;
|
|
107
127
|
}
|
|
108
128
|
catch (error) {
|
|
109
129
|
const status = error.status || error.response?.status;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readSourceText.d.ts","sourceRoot":"","sources":["../../../src/lib/backup/readSourceText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,QAAQ,GAAG,UAAqB,GACxC,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"readSourceText.d.ts","sourceRoot":"","sources":["../../../src/lib/backup/readSourceText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,QAAQ,GAAG,UAAqB,GACxC,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CA8GpC"}
|
|
@@ -23,6 +23,12 @@ async function readSourceText(client, spec, version = 'active') {
|
|
|
23
23
|
.read({ programName: spec.name }, version);
|
|
24
24
|
return (0, responseToText_1.responseToText)(state?.readResult);
|
|
25
25
|
}
|
|
26
|
+
case 'transformation': {
|
|
27
|
+
const state = await client
|
|
28
|
+
.getTransformation()
|
|
29
|
+
.read({ transformationName: spec.name }, version);
|
|
30
|
+
return (0, responseToText_1.responseToText)(state?.readResult);
|
|
31
|
+
}
|
|
26
32
|
case 'view': {
|
|
27
33
|
const state = await client
|
|
28
34
|
.getView()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeOrder.d.ts","sourceRoot":"","sources":["../../../src/lib/constants/typeOrder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,eAAO,MAAM,SAAS,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"typeOrder.d.ts","sourceRoot":"","sources":["../../../src/lib/constants/typeOrder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,eAAO,MAAM,SAAS,EAAE,aAAa,EAoBpC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collectTreeDependencies.d.ts","sourceRoot":"","sources":["../../../src/lib/dependencies/collectTreeDependencies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAK3D,OAAO,KAAK,EAAE,cAAc,EAA6B,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"collectTreeDependencies.d.ts","sourceRoot":"","sources":["../../../src/lib/dependencies/collectTreeDependencies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAK3D,OAAO,KAAK,EAAE,cAAc,EAA6B,MAAM,UAAU,CAAC;AAoC1E,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Ff"}
|
|
@@ -10,4 +10,10 @@ export interface RestoreGroup {
|
|
|
10
10
|
* (e.g. view and behaviorDefinition for the same CDS entity).
|
|
11
11
|
*/
|
|
12
12
|
export declare function analyzeDependencies(nodes: BackupTreeNode[]): RestoreGroup[];
|
|
13
|
+
/**
|
|
14
|
+
* Analyzes dependencies and merges SCCs at the same dependency level
|
|
15
|
+
* into single groups. Level = max(level of dependencies) + 1.
|
|
16
|
+
* Independent SCCs (same level) are merged into one group.
|
|
17
|
+
*/
|
|
18
|
+
export declare function analyzeDependencyLevels(nodes: BackupTreeNode[]): RestoreGroup[];
|
|
13
19
|
//# sourceMappingURL=analyzeDependencies.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzeDependencies.d.ts","sourceRoot":"","sources":["../../../src/lib/restore/analyzeDependencies.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"analyzeDependencies.d.ts","sourceRoot":"","sources":["../../../src/lib/restore/analyzeDependencies.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB;AA+MD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,YAAY,EAAE,CA2B3E;AA4BD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,cAAc,EAAE,GACtB,YAAY,EAAE,CA8DhB"}
|
|
@@ -1,30 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.analyzeDependencies = analyzeDependencies;
|
|
4
|
+
exports.analyzeDependencyLevels = analyzeDependencyLevels;
|
|
4
5
|
const decodeBase64_1 = require("../crypto/decodeBase64");
|
|
5
6
|
function nodeKey(node) {
|
|
6
7
|
return `${node.type}:${node.name}`.toUpperCase();
|
|
7
8
|
}
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
* source code and XML metadata.
|
|
11
|
-
* Uses composite type:name keys to handle objects that share the same name
|
|
12
|
-
* (e.g. view and behaviorDefinition for the same CDS entity).
|
|
10
|
+
* Build adjacency map (forward dependencies) by scanning source code and config.
|
|
13
11
|
*/
|
|
14
|
-
function
|
|
15
|
-
const idToNode = new Map();
|
|
16
|
-
const nameToIds = new Map();
|
|
17
|
-
const allNames = new Set();
|
|
18
|
-
for (const node of nodes) {
|
|
19
|
-
const id = nodeKey(node);
|
|
20
|
-
const upperName = node.name.toUpperCase();
|
|
21
|
-
idToNode.set(id, node);
|
|
22
|
-
allNames.add(upperName);
|
|
23
|
-
const ids = nameToIds.get(upperName) || [];
|
|
24
|
-
ids.push(id);
|
|
25
|
-
nameToIds.set(upperName, ids);
|
|
26
|
-
}
|
|
27
|
-
const allIds = new Set(idToNode.keys());
|
|
12
|
+
function buildAdjacency(nodes, allNames, nameToIds, allIds) {
|
|
28
13
|
const adj = new Map();
|
|
29
14
|
for (const node of nodes) {
|
|
30
15
|
const deps = new Set();
|
|
@@ -37,13 +22,9 @@ function analyzeDependencies(nodes) {
|
|
|
37
22
|
for (const targetName of allNames) {
|
|
38
23
|
if (targetName === nodeNameUpper)
|
|
39
24
|
continue;
|
|
40
|
-
// Escape name for regex, specifically handling namespaces with /
|
|
41
25
|
const escapedTarget = targetName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
42
|
-
// Match only if surrounded by boundaries that are NOT part of an ABAP name
|
|
43
|
-
// (i.e. not letters, digits, _, or /)
|
|
44
26
|
const regex = new RegExp(`(?<=[^A-Z0-9_/]|^)${escapedTarget}(?=[^A-Z0-9_/]|$)`, 'g');
|
|
45
27
|
if (regex.test(contentUpper)) {
|
|
46
|
-
// Resolve name to all node IDs with that name
|
|
47
28
|
const targetIds = nameToIds.get(targetName) || [];
|
|
48
29
|
for (const tid of targetIds) {
|
|
49
30
|
if (tid !== id)
|
|
@@ -51,9 +32,8 @@ function analyzeDependencies(nodes) {
|
|
|
51
32
|
}
|
|
52
33
|
}
|
|
53
34
|
}
|
|
54
|
-
// 2. Explicit structural dependencies
|
|
55
|
-
//
|
|
56
|
-
// From class source: "FOR BEHAVIOR OF <bdef_name>"
|
|
35
|
+
// 2. Explicit structural dependencies
|
|
36
|
+
// BIML class -> BDEF (bidirectional = same SCC)
|
|
57
37
|
if (node.type === 'class' || node.type === 'behaviorImplementation') {
|
|
58
38
|
const bdefMatch = contentUpper.match(/FOR\s+BEHAVIOR\s+OF\s+([A-Z0-9_/]+)/);
|
|
59
39
|
if (bdefMatch) {
|
|
@@ -62,7 +42,7 @@ function analyzeDependencies(nodes) {
|
|
|
62
42
|
deps.add(bdefId);
|
|
63
43
|
}
|
|
64
44
|
}
|
|
65
|
-
//
|
|
45
|
+
// BDEF -> BIML class
|
|
66
46
|
if (node.type === 'behaviorDefinition') {
|
|
67
47
|
for (const m of contentUpper.matchAll(/IMPLEMENTATION\s+IN\s+CLASS\s+([A-Z0-9_/]+)/g)) {
|
|
68
48
|
const className = m[1];
|
|
@@ -93,7 +73,12 @@ function analyzeDependencies(nodes) {
|
|
|
93
73
|
}
|
|
94
74
|
adj.set(id, deps);
|
|
95
75
|
}
|
|
96
|
-
|
|
76
|
+
return adj;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Tarjan's SCC algorithm.
|
|
80
|
+
*/
|
|
81
|
+
function tarjanSCC(allIds, adj) {
|
|
97
82
|
let index = 0;
|
|
98
83
|
const stack = [];
|
|
99
84
|
const onStack = new Set();
|
|
@@ -140,7 +125,12 @@ function analyzeDependencies(nodes) {
|
|
|
140
125
|
strongConnect(id);
|
|
141
126
|
}
|
|
142
127
|
}
|
|
143
|
-
|
|
128
|
+
return sccs;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build SCC DAG and topological order from SCCs and adjacency map.
|
|
132
|
+
*/
|
|
133
|
+
function buildSccDag(sccs, adj) {
|
|
144
134
|
const sccAdj = new Map();
|
|
145
135
|
const nodeToSccIndex = new Map();
|
|
146
136
|
sccs.forEach((scc, i) => {
|
|
@@ -177,6 +167,31 @@ function analyzeDependencies(nodes) {
|
|
|
177
167
|
for (let i = 0; i < sccs.length; i++) {
|
|
178
168
|
visit(i);
|
|
179
169
|
}
|
|
170
|
+
return { sccAdj, order };
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Robustly analyzes dependencies between objects by scanning both
|
|
174
|
+
* source code and XML metadata.
|
|
175
|
+
* Uses composite type:name keys to handle objects that share the same name
|
|
176
|
+
* (e.g. view and behaviorDefinition for the same CDS entity).
|
|
177
|
+
*/
|
|
178
|
+
function analyzeDependencies(nodes) {
|
|
179
|
+
const idToNode = new Map();
|
|
180
|
+
const nameToIds = new Map();
|
|
181
|
+
const allNames = new Set();
|
|
182
|
+
for (const node of nodes) {
|
|
183
|
+
const id = nodeKey(node);
|
|
184
|
+
const upperName = node.name.toUpperCase();
|
|
185
|
+
idToNode.set(id, node);
|
|
186
|
+
allNames.add(upperName);
|
|
187
|
+
const ids = nameToIds.get(upperName) || [];
|
|
188
|
+
ids.push(id);
|
|
189
|
+
nameToIds.set(upperName, ids);
|
|
190
|
+
}
|
|
191
|
+
const allIds = new Set(idToNode.keys());
|
|
192
|
+
const adj = buildAdjacency(nodes, allNames, nameToIds, allIds);
|
|
193
|
+
const sccs = tarjanSCC(allIds, adj);
|
|
194
|
+
const { order } = buildSccDag(sccs, adj);
|
|
180
195
|
return order.map((i) => {
|
|
181
196
|
const ids = sccs[i];
|
|
182
197
|
return {
|
|
@@ -185,3 +200,87 @@ function analyzeDependencies(nodes) {
|
|
|
185
200
|
};
|
|
186
201
|
});
|
|
187
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Creation order priority within a group.
|
|
205
|
+
* Lower = created first. Ensures BDEF exists before BIML class, etc.
|
|
206
|
+
*/
|
|
207
|
+
const TYPE_CREATION_ORDER = {
|
|
208
|
+
domain: 0,
|
|
209
|
+
dataElement: 1,
|
|
210
|
+
structure: 2,
|
|
211
|
+
table: 2,
|
|
212
|
+
tableType: 2,
|
|
213
|
+
view: 3,
|
|
214
|
+
behaviorDefinition: 4,
|
|
215
|
+
behaviorImplementation: 5,
|
|
216
|
+
class: 5,
|
|
217
|
+
interface: 5,
|
|
218
|
+
accessControl: 6,
|
|
219
|
+
metadataExtension: 6,
|
|
220
|
+
program: 7,
|
|
221
|
+
transformation: 7,
|
|
222
|
+
functionGroup: 7,
|
|
223
|
+
functionModule: 8,
|
|
224
|
+
serviceDefinition: 9,
|
|
225
|
+
serviceBinding: 10,
|
|
226
|
+
enhancement: 11,
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Analyzes dependencies and merges SCCs at the same dependency level
|
|
230
|
+
* into single groups. Level = max(level of dependencies) + 1.
|
|
231
|
+
* Independent SCCs (same level) are merged into one group.
|
|
232
|
+
*/
|
|
233
|
+
function analyzeDependencyLevels(nodes) {
|
|
234
|
+
const idToNode = new Map();
|
|
235
|
+
const nameToIds = new Map();
|
|
236
|
+
const allNames = new Set();
|
|
237
|
+
for (const node of nodes) {
|
|
238
|
+
const id = nodeKey(node);
|
|
239
|
+
const upperName = node.name.toUpperCase();
|
|
240
|
+
idToNode.set(id, node);
|
|
241
|
+
allNames.add(upperName);
|
|
242
|
+
const ids = nameToIds.get(upperName) || [];
|
|
243
|
+
ids.push(id);
|
|
244
|
+
nameToIds.set(upperName, ids);
|
|
245
|
+
}
|
|
246
|
+
const allIds = new Set(idToNode.keys());
|
|
247
|
+
const adj = buildAdjacency(nodes, allNames, nameToIds, allIds);
|
|
248
|
+
const sccs = tarjanSCC(allIds, adj);
|
|
249
|
+
const { sccAdj, order } = buildSccDag(sccs, adj);
|
|
250
|
+
// Compute level for each SCC: level = max(level of deps) + 1
|
|
251
|
+
const sccLevel = new Map();
|
|
252
|
+
for (const i of order) {
|
|
253
|
+
let maxDepLevel = -1;
|
|
254
|
+
const deps = sccAdj.get(i) || new Set();
|
|
255
|
+
for (const d of deps) {
|
|
256
|
+
const depLevel = sccLevel.get(d) ?? 0;
|
|
257
|
+
if (depLevel > maxDepLevel)
|
|
258
|
+
maxDepLevel = depLevel;
|
|
259
|
+
}
|
|
260
|
+
sccLevel.set(i, maxDepLevel + 1);
|
|
261
|
+
}
|
|
262
|
+
// Group SCCs by level, merge into RestoreGroups
|
|
263
|
+
const levelMap = new Map();
|
|
264
|
+
for (const i of order) {
|
|
265
|
+
const level = sccLevel.get(i);
|
|
266
|
+
if (!levelMap.has(level)) {
|
|
267
|
+
levelMap.set(level, { nodes: [], hasCircular: false });
|
|
268
|
+
}
|
|
269
|
+
const entry = levelMap.get(level);
|
|
270
|
+
const sccNodes = sccs[i].map((id) => idToNode.get(id));
|
|
271
|
+
entry.nodes.push(...sccNodes);
|
|
272
|
+
if (sccs[i].length > 1)
|
|
273
|
+
entry.hasCircular = true;
|
|
274
|
+
}
|
|
275
|
+
const sortedLevels = [...levelMap.keys()].sort((a, b) => a - b);
|
|
276
|
+
return sortedLevels.map((level) => {
|
|
277
|
+
const entry = levelMap.get(level);
|
|
278
|
+
// Sort nodes within group by creation order (views before BDEFs before classes)
|
|
279
|
+
entry.nodes.sort((a, b) => (TYPE_CREATION_ORDER[a.type || ''] ?? 99) -
|
|
280
|
+
(TYPE_CREATION_ORDER[b.type || ''] ?? 99));
|
|
281
|
+
return {
|
|
282
|
+
nodes: entry.nodes,
|
|
283
|
+
isCircular: entry.hasCircular || entry.nodes.length > 1,
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restoreObject.d.ts","sourceRoot":"","sources":["../../../src/lib/restore/restoreObject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,
|
|
1
|
+
{"version":3,"file":"restoreObject.d.ts","sourceRoot":"","sources":["../../../src/lib/restore/restoreObject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EAoBV,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAO1D,wBAAsB,aAAa,CACjC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,OAAO,EACjB,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC,CA2Tf"}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.restoreObject = restoreObject;
|
|
4
4
|
const applyConfigName_1 = require("../utils/applyConfigName");
|
|
5
5
|
const asConfig_1 = require("../utils/asConfig");
|
|
6
|
+
const detectTransformationType_1 = require("../utils/detectTransformationType");
|
|
6
7
|
const ensureDescription_1 = require("../utils/ensureDescription");
|
|
7
8
|
const applyTransportRequest_1 = require("./applyTransportRequest");
|
|
8
9
|
async function restoreObject(client, obj, mode, activate, transportRequest) {
|
|
@@ -128,6 +129,25 @@ async function restoreObject(client, obj, mode, activate, transportRequest) {
|
|
|
128
129
|
}
|
|
129
130
|
return;
|
|
130
131
|
}
|
|
132
|
+
case 'transformation': {
|
|
133
|
+
const transformationType = (0, detectTransformationType_1.detectTransformationType)(obj.source);
|
|
134
|
+
const baseTxConfig = {
|
|
135
|
+
...config,
|
|
136
|
+
transformationType,
|
|
137
|
+
};
|
|
138
|
+
if (mode !== 'update') {
|
|
139
|
+
await client
|
|
140
|
+
.getTransformation()
|
|
141
|
+
.create((0, asConfig_1.asConfig)(baseTxConfig), options);
|
|
142
|
+
}
|
|
143
|
+
if (obj.source) {
|
|
144
|
+
await client.getTransformation().update((0, asConfig_1.asConfig)({
|
|
145
|
+
...baseTxConfig,
|
|
146
|
+
sourceCode: obj.source,
|
|
147
|
+
}), options);
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
131
151
|
case 'functionGroup': {
|
|
132
152
|
if (mode !== 'update') {
|
|
133
153
|
await client
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restoreObjects.d.ts","sourceRoot":"","sources":["../../../src/lib/restore/restoreObjects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,2BAA2B,CAAC;AAE5E,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"restoreObjects.d.ts","sourceRoot":"","sources":["../../../src/lib/restore/restoreObjects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,2BAA2B,CAAC;AAE5E,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AA4B1D,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,YAAY,EAAE,EACvB,IAAI,EAAE,WAAW,EACjB,gBAAgB,EAAE,OAAO,EACzB,gBAAgB,CAAC,EAAE,MAAM,EACzB,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,EACzC,gBAAgB,UAAO,GACtB,OAAO,CAAC,IAAI,CAAC,CA+Df"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { AdtClient } from '@mcp-abap-adt/adt-clients';
|
|
2
|
-
import type { BackupTreeNode, RestoreMode } from '../types';
|
|
3
|
-
export declare function restoreTreeBackup(client: AdtClient, root: BackupTreeNode, mode: RestoreMode, activate: boolean, transportRequest?: string, restoreIds?: Set<string>,
|
|
2
|
+
import type { BackupTreeNode, RestoreMode, RestorePlanGroup } from '../types';
|
|
3
|
+
export declare function restoreTreeBackup(client: AdtClient, root: BackupTreeNode, mode: RestoreMode, activate: boolean, transportRequest?: string, restoreIds?: Set<string>, planGroups?: RestorePlanGroup[], activateOnCreate?: boolean, softwareComponent?: string, superPackageOverride?: string, transportLayer?: string): Promise<void>;
|
|
4
4
|
//# sourceMappingURL=restoreTreeBackup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restoreTreeBackup.d.ts","sourceRoot":"","sources":["../../../src/lib/restore/restoreTreeBackup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"restoreTreeBackup.d.ts","sourceRoot":"","sources":["../../../src/lib/restore/restoreTreeBackup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,2BAA2B,CAAC;AAK5E,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,gBAAgB,EAEjB,MAAM,UAAU,CAAC;AAmElB,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,OAAO,EACjB,gBAAgB,CAAC,EAAE,MAAM,EACzB,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,UAAU,CAAC,EAAE,gBAAgB,EAAE,EAC/B,gBAAgB,UAAO,EACvB,iBAAiB,CAAC,EAAE,MAAM,EAC1B,oBAAoB,CAAC,EAAE,MAAM,EAC7B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAiZf"}
|