@sap-ux/fiori-mcp-server 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +99 -0
  3. package/dist/constant.d.ts +5 -0
  4. package/dist/constant.js +8 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.js +7 -0
  7. package/dist/page-editor-api/api.d.ts +48 -0
  8. package/dist/page-editor-api/api.js +93 -0
  9. package/dist/page-editor-api/index.d.ts +5 -0
  10. package/dist/page-editor-api/index.js +24 -0
  11. package/dist/page-editor-api/json-helper.d.ts +11 -0
  12. package/dist/page-editor-api/json-helper.js +64 -0
  13. package/dist/page-editor-api/parser/annotations.d.ts +9 -0
  14. package/dist/page-editor-api/parser/annotations.js +13 -0
  15. package/dist/page-editor-api/parser/index.d.ts +3 -0
  16. package/dist/page-editor-api/parser/index.js +19 -0
  17. package/dist/page-editor-api/parser/model/AggregationValidator.d.ts +64 -0
  18. package/dist/page-editor-api/parser/model/AggregationValidator.js +209 -0
  19. package/dist/page-editor-api/parser/model/ArrayAggregation.d.ts +49 -0
  20. package/dist/page-editor-api/parser/model/ArrayAggregation.js +122 -0
  21. package/dist/page-editor-api/parser/model/ObjectAggregation.d.ts +374 -0
  22. package/dist/page-editor-api/parser/model/ObjectAggregation.js +802 -0
  23. package/dist/page-editor-api/parser/model/PageEditModel.d.ts +223 -0
  24. package/dist/page-editor-api/parser/model/PageEditModel.js +954 -0
  25. package/dist/page-editor-api/parser/model/PageEditProperty.d.ts +38 -0
  26. package/dist/page-editor-api/parser/model/PageEditProperty.js +49 -0
  27. package/dist/page-editor-api/parser/model/RootAggregation.d.ts +24 -0
  28. package/dist/page-editor-api/parser/model/RootAggregation.js +54 -0
  29. package/dist/page-editor-api/parser/model/actions/ActionAggregation.d.ts +34 -0
  30. package/dist/page-editor-api/parser/model/actions/ActionAggregation.js +92 -0
  31. package/dist/page-editor-api/parser/model/actions/ActionsAggregation.d.ts +96 -0
  32. package/dist/page-editor-api/parser/model/actions/ActionsAggregation.js +252 -0
  33. package/dist/page-editor-api/parser/model/actions/index.d.ts +3 -0
  34. package/dist/page-editor-api/parser/model/actions/index.js +19 -0
  35. package/dist/page-editor-api/parser/model/additionalObjects/AdditionalObjectAggregation.d.ts +17 -0
  36. package/dist/page-editor-api/parser/model/additionalObjects/AdditionalObjectAggregation.js +26 -0
  37. package/dist/page-editor-api/parser/model/additionalObjects/AdditionalObjectsAggregation.d.ts +46 -0
  38. package/dist/page-editor-api/parser/model/additionalObjects/AdditionalObjectsAggregation.js +66 -0
  39. package/dist/page-editor-api/parser/model/additionalObjects/index.d.ts +3 -0
  40. package/dist/page-editor-api/parser/model/additionalObjects/index.js +19 -0
  41. package/dist/page-editor-api/parser/model/chart/ChartAggregation.d.ts +41 -0
  42. package/dist/page-editor-api/parser/model/chart/ChartAggregation.js +94 -0
  43. package/dist/page-editor-api/parser/model/chart/index.d.ts +2 -0
  44. package/dist/page-editor-api/parser/model/chart/index.js +18 -0
  45. package/dist/page-editor-api/parser/model/fields/ConnectedFieldsAggregation.d.ts +9 -0
  46. package/dist/page-editor-api/parser/model/fields/ConnectedFieldsAggregation.js +13 -0
  47. package/dist/page-editor-api/parser/model/fields/FieldAggregation.d.ts +25 -0
  48. package/dist/page-editor-api/parser/model/fields/FieldAggregation.js +42 -0
  49. package/dist/page-editor-api/parser/model/fields/FieldsAggregation.d.ts +22 -0
  50. package/dist/page-editor-api/parser/model/fields/FieldsAggregation.js +34 -0
  51. package/dist/page-editor-api/parser/model/fields/index.d.ts +4 -0
  52. package/dist/page-editor-api/parser/model/fields/index.js +20 -0
  53. package/dist/page-editor-api/parser/model/filter-fields/FilterFieldAggregation.d.ts +39 -0
  54. package/dist/page-editor-api/parser/model/filter-fields/FilterFieldAggregation.js +94 -0
  55. package/dist/page-editor-api/parser/model/filter-fields/FilterFieldsAggregation.d.ts +36 -0
  56. package/dist/page-editor-api/parser/model/filter-fields/FilterFieldsAggregation.js +59 -0
  57. package/dist/page-editor-api/parser/model/filter-fields/index.d.ts +3 -0
  58. package/dist/page-editor-api/parser/model/filter-fields/index.js +19 -0
  59. package/dist/page-editor-api/parser/model/index.d.ts +19 -0
  60. package/dist/page-editor-api/parser/model/index.js +35 -0
  61. package/dist/page-editor-api/parser/model/macros/MacrosRoot.d.ts +48 -0
  62. package/dist/page-editor-api/parser/model/macros/MacrosRoot.js +114 -0
  63. package/dist/page-editor-api/parser/model/macros/index.d.ts +2 -0
  64. package/dist/page-editor-api/parser/model/macros/index.js +18 -0
  65. package/dist/page-editor-api/parser/model/sections/HeaderSectionsAggregation.d.ts +31 -0
  66. package/dist/page-editor-api/parser/model/sections/HeaderSectionsAggregation.js +82 -0
  67. package/dist/page-editor-api/parser/model/sections/SectionAggregation.d.ts +78 -0
  68. package/dist/page-editor-api/parser/model/sections/SectionAggregation.js +131 -0
  69. package/dist/page-editor-api/parser/model/sections/SectionsAggregation.d.ts +135 -0
  70. package/dist/page-editor-api/parser/model/sections/SectionsAggregation.js +402 -0
  71. package/dist/page-editor-api/parser/model/sections/SectionsObjectAggregation.d.ts +50 -0
  72. package/dist/page-editor-api/parser/model/sections/SectionsObjectAggregation.js +119 -0
  73. package/dist/page-editor-api/parser/model/sections/SubSectionsAggregation.d.ts +39 -0
  74. package/dist/page-editor-api/parser/model/sections/SubSectionsAggregation.js +70 -0
  75. package/dist/page-editor-api/parser/model/sections/index.d.ts +6 -0
  76. package/dist/page-editor-api/parser/model/sections/index.js +22 -0
  77. package/dist/page-editor-api/parser/model/table/ColumnAggregation.d.ts +89 -0
  78. package/dist/page-editor-api/parser/model/table/ColumnAggregation.js +175 -0
  79. package/dist/page-editor-api/parser/model/table/ColumnsAggregation.d.ts +113 -0
  80. package/dist/page-editor-api/parser/model/table/ColumnsAggregation.js +293 -0
  81. package/dist/page-editor-api/parser/model/table/TableAggregation.d.ts +13 -0
  82. package/dist/page-editor-api/parser/model/table/TableAggregation.js +21 -0
  83. package/dist/page-editor-api/parser/model/table/ToolbarAggregation.d.ts +15 -0
  84. package/dist/page-editor-api/parser/model/table/ToolbarAggregation.js +22 -0
  85. package/dist/page-editor-api/parser/model/table/index.d.ts +5 -0
  86. package/dist/page-editor-api/parser/model/table/index.js +21 -0
  87. package/dist/page-editor-api/parser/model/table/utils.d.ts +12 -0
  88. package/dist/page-editor-api/parser/model/table/utils.js +44 -0
  89. package/dist/page-editor-api/parser/model/types/annotations.d.ts +63 -0
  90. package/dist/page-editor-api/parser/model/types/annotations.js +29 -0
  91. package/dist/page-editor-api/parser/model/types/common.d.ts +13 -0
  92. package/dist/page-editor-api/parser/model/types/common.js +3 -0
  93. package/dist/page-editor-api/parser/model/types/index.d.ts +220 -0
  94. package/dist/page-editor-api/parser/model/types/index.js +149 -0
  95. package/dist/page-editor-api/parser/model/utils/annotations.d.ts +38 -0
  96. package/dist/page-editor-api/parser/model/utils/annotations.js +120 -0
  97. package/dist/page-editor-api/parser/model/utils/i18n.d.ts +33 -0
  98. package/dist/page-editor-api/parser/model/utils/i18n.js +69 -0
  99. package/dist/page-editor-api/parser/model/utils/index.d.ts +6 -0
  100. package/dist/page-editor-api/parser/model/utils/index.js +22 -0
  101. package/dist/page-editor-api/parser/model/utils/object.d.ts +25 -0
  102. package/dist/page-editor-api/parser/model/utils/object.js +68 -0
  103. package/dist/page-editor-api/parser/model/utils/sort.d.ts +31 -0
  104. package/dist/page-editor-api/parser/model/utils/sort.js +18 -0
  105. package/dist/page-editor-api/parser/model/utils/utils.d.ts +94 -0
  106. package/dist/page-editor-api/parser/model/utils/utils.js +267 -0
  107. package/dist/page-editor-api/parser/model/views/ViewAggregation.d.ts +62 -0
  108. package/dist/page-editor-api/parser/model/views/ViewAggregation.js +112 -0
  109. package/dist/page-editor-api/parser/model/views/ViewsAggregation.d.ts +54 -0
  110. package/dist/page-editor-api/parser/model/views/ViewsAggregation.js +141 -0
  111. package/dist/page-editor-api/parser/model/views/index.d.ts +3 -0
  112. package/dist/page-editor-api/parser/model/views/index.js +19 -0
  113. package/dist/page-editor-api/parser/model/visual-filters/VisualFilterAggregation.d.ts +11 -0
  114. package/dist/page-editor-api/parser/model/visual-filters/VisualFilterAggregation.js +15 -0
  115. package/dist/page-editor-api/parser/model/visual-filters/VisualFiltersAggregation.d.ts +11 -0
  116. package/dist/page-editor-api/parser/model/visual-filters/VisualFiltersAggregation.js +15 -0
  117. package/dist/page-editor-api/parser/model/visual-filters/index.d.ts +3 -0
  118. package/dist/page-editor-api/parser/model/visual-filters/index.js +19 -0
  119. package/dist/page-editor-api/parser/tree.d.ts +135 -0
  120. package/dist/page-editor-api/parser/tree.js +464 -0
  121. package/dist/page-editor-api/project.d.ts +40 -0
  122. package/dist/page-editor-api/project.js +124 -0
  123. package/dist/page-editor-api/sapuxFtfsFileIO.d.ts +84 -0
  124. package/dist/page-editor-api/sapuxFtfsFileIO.js +195 -0
  125. package/dist/server.d.ts +35 -0
  126. package/dist/server.js +120 -0
  127. package/dist/tools/execute-functionality.d.ts +19 -0
  128. package/dist/tools/execute-functionality.js +175 -0
  129. package/dist/tools/functionalities/controller-extension/index.d.ts +4 -0
  130. package/dist/tools/functionalities/controller-extension/index.js +136 -0
  131. package/dist/tools/functionalities/functionalities.d.ts +4 -0
  132. package/dist/tools/functionalities/functionalities.js +19 -0
  133. package/dist/tools/functionalities/generate-fiori-ui-app/command.d.ts +9 -0
  134. package/dist/tools/functionalities/generate-fiori-ui-app/command.js +158 -0
  135. package/dist/tools/functionalities/generate-fiori-ui-app/generate-fiori-ui-app.d.ts +4 -0
  136. package/dist/tools/functionalities/generate-fiori-ui-app/generate-fiori-ui-app.js +240 -0
  137. package/dist/tools/functionalities/generate-fiori-ui-app/index.d.ts +2 -0
  138. package/dist/tools/functionalities/generate-fiori-ui-app/index.js +7 -0
  139. package/dist/tools/functionalities/index.d.ts +2 -0
  140. package/dist/tools/functionalities/index.js +18 -0
  141. package/dist/tools/functionalities/page/add-page.d.ts +5 -0
  142. package/dist/tools/functionalities/page/add-page.js +89 -0
  143. package/dist/tools/functionalities/page/application.d.ts +212 -0
  144. package/dist/tools/functionalities/page/application.js +616 -0
  145. package/dist/tools/functionalities/page/delete-page.d.ts +4 -0
  146. package/dist/tools/functionalities/page/delete-page.js +71 -0
  147. package/dist/tools/functionalities/page/index.d.ts +3 -0
  148. package/dist/tools/functionalities/page/index.js +10 -0
  149. package/dist/tools/functionalities/page/service.d.ts +82 -0
  150. package/dist/tools/functionalities/page/service.js +114 -0
  151. package/dist/tools/functionalities/page/serviceStore.d.ts +17 -0
  152. package/dist/tools/functionalities/page/serviceStore.js +34 -0
  153. package/dist/tools/functionalities/page/types.d.ts +42 -0
  154. package/dist/tools/functionalities/page/types.js +11 -0
  155. package/dist/tools/functionalities/page/utils.d.ts +12 -0
  156. package/dist/tools/functionalities/page/utils.js +63 -0
  157. package/dist/tools/get-functionality-details.d.ts +24 -0
  158. package/dist/tools/get-functionality-details.js +142 -0
  159. package/dist/tools/index.d.ts +7 -0
  160. package/dist/tools/index.js +55 -0
  161. package/dist/tools/input-schema/execute-functionality.json +28 -0
  162. package/dist/tools/input-schema/get-functionality-details.json +24 -0
  163. package/dist/tools/input-schema/index.d.ts +5 -0
  164. package/dist/tools/input-schema/index.js +15 -0
  165. package/dist/tools/input-schema/list-fiori-apps.json +12 -0
  166. package/dist/tools/input-schema/list-functionality.json +10 -0
  167. package/dist/tools/list-fiori-apps.d.ts +10 -0
  168. package/dist/tools/list-fiori-apps.js +33 -0
  169. package/dist/tools/list-functionalities.d.ts +10 -0
  170. package/dist/tools/list-functionalities.js +145 -0
  171. package/dist/tools/output-schema/execute-functionality.json +39 -0
  172. package/dist/tools/output-schema/get-functionality-details.json +142 -0
  173. package/dist/tools/output-schema/index.d.ts +5 -0
  174. package/dist/tools/output-schema/index.js +15 -0
  175. package/dist/tools/output-schema/list-fiori-apps.json +41 -0
  176. package/dist/tools/output-schema/list-functionality.json +37 -0
  177. package/dist/tools/utils.d.ts +16 -0
  178. package/dist/tools/utils.js +74 -0
  179. package/dist/types.d.ts +170 -0
  180. package/dist/types.js +3 -0
  181. package/package.json +63 -0
@@ -0,0 +1,616 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Application = exports.DELETE_PAGE_FUNCTIONALITY = exports.ADD_PAGE_FUNCTIONALITY = void 0;
4
+ const src_1 = require("@sap/ux-specification/dist/types/src");
5
+ const page_editor_api_1 = require("../../../page-editor-api");
6
+ const serviceStore_1 = require("./serviceStore");
7
+ const types_1 = require("./types");
8
+ const utils_1 = require("./utils");
9
+ const project_access_1 = require("@sap-ux/project-access");
10
+ const path_1 = require("path");
11
+ const constant_1 = require("../../../constant");
12
+ const utils_2 = require("../../utils");
13
+ exports.ADD_PAGE_FUNCTIONALITY = {
14
+ functionalityId: constant_1.ADD_PAGE,
15
+ name: 'Add new page to application by updating manifest.json',
16
+ description: 'Create new fiori elements page like ListReport, ObjectPage, CustomPage',
17
+ parameters: []
18
+ };
19
+ exports.DELETE_PAGE_FUNCTIONALITY = {
20
+ functionalityId: constant_1.DELETE_PAGE,
21
+ name: 'Delete page from application by updating manifest.json',
22
+ description: 'Remove existing fiori elements page from the application',
23
+ parameters: []
24
+ };
25
+ /**
26
+ * Represents an application instance with its metadata, access configuration and functionality details.
27
+ */
28
+ class Application {
29
+ serviceName;
30
+ appData;
31
+ appId;
32
+ applicationAccess;
33
+ params;
34
+ /**
35
+ * Creates a new instance of the Application class.
36
+ *
37
+ * @param root0 - The constructor arguments.
38
+ * @param root0.params - Input parameters for functionality details.
39
+ * @param root0.applicationAccess - The application access object.
40
+ * @param root0.appId - The unique identifier of the application.
41
+ * @param root0.serviceName - The service name associated with the application.
42
+ * @param root0.appData - Metadata and configuration data for the application.
43
+ */
44
+ constructor({ params, applicationAccess, appId, serviceName, appData }) {
45
+ this.params = params;
46
+ this.serviceName = serviceName;
47
+ this.appData = appData;
48
+ this.appId = appId;
49
+ this.applicationAccess = applicationAccess;
50
+ }
51
+ /**
52
+ * Gets the name of an allowed navigation option.
53
+ *
54
+ * @param navigation - The allowed navigation option.
55
+ * @returns The name of the navigation option.
56
+ */
57
+ getAllowedNavigationsName(navigation) {
58
+ return navigation.name;
59
+ }
60
+ /**
61
+ * Generates a string representation of all allowed navigation options.
62
+ *
63
+ * @param navigations - An array of allowed navigation options.
64
+ * @returns A comma-separated string of navigation names.
65
+ */
66
+ getAllowedNavigationsOutput(navigations) {
67
+ return navigations.map((navigation) => this.getAllowedNavigationsName(navigation)).join(', ');
68
+ }
69
+ /**
70
+ * Generates an error message for missing navigation.
71
+ *
72
+ * @param reason - The reason for the missing navigation.
73
+ * @param navigations - An array of allowed navigation options.
74
+ * @param parentPageId - Optional ID of the parent page.
75
+ * @returns An error message string.
76
+ */
77
+ getMissingNavigationMessage(reason, navigations, parentPageId) {
78
+ let message = '';
79
+ switch (reason) {
80
+ case types_1.MissingNavigationReason.NoAnyNavigationsForParent: {
81
+ message = 'page does not have valid navigation for add';
82
+ break;
83
+ }
84
+ case types_1.MissingNavigationReason.NotFoundNavigationForParent: {
85
+ message = `provided 'navigation' was not found. ${parentPageId} has following navigations available: ${this.getAllowedNavigationsOutput(navigations)}`;
86
+ break;
87
+ }
88
+ case types_1.MissingNavigationReason.NoEntityProvided: {
89
+ message = `provide 'entitySet' for very first page`;
90
+ break;
91
+ }
92
+ case types_1.MissingNavigationReason.NotFoundEntity: {
93
+ message = `provided 'entitySet' was not found. Following entitySets available: ${this.getAllowedNavigationsOutput(navigations)}`;
94
+ break;
95
+ }
96
+ }
97
+ return message;
98
+ }
99
+ /**
100
+ * Calculates the context path for a page.
101
+ *
102
+ * @param parentPage - Optional parent page definition.
103
+ * @param navigationProperty - Optional navigation property.
104
+ * @param entitySet - Optional entity set.
105
+ * @returns The calculated context path or undefined.
106
+ */
107
+ calculateContextPath(parentPage, navigationProperty, entitySet) {
108
+ let contextPath;
109
+ if (!parentPage || parentPage.pageType === src_1.PageTypeV4.ListReport) {
110
+ contextPath = `/${entitySet}`;
111
+ }
112
+ else if (parentPage.contextPath) {
113
+ contextPath =
114
+ navigationProperty && parentPage.routePattern !== ':?query:'
115
+ ? `${parentPage.contextPath}/${navigationProperty}`
116
+ : `/${entitySet}`;
117
+ }
118
+ else if (parentPage.entitySet) {
119
+ contextPath = this.calculateContextPathBasedOnEntitySet(parentPage, navigationProperty);
120
+ }
121
+ return contextPath;
122
+ }
123
+ /**
124
+ * Calculates the context path based on the entity set of the parent page.
125
+ *
126
+ * @param parentPage - The parent page definition.
127
+ * @param navigationProperty - Optional navigation property.
128
+ * @returns The calculated context path or undefined.
129
+ */
130
+ calculateContextPathBasedOnEntitySet(parentPage, navigationProperty) {
131
+ let contextPath;
132
+ const routePattern = parentPage.routePattern;
133
+ const convertedParentPattern = routePattern?.replace(':?query:', '').replace(/\({[^}]*}\)/g, '');
134
+ if (!convertedParentPattern) {
135
+ contextPath =
136
+ navigationProperty && navigationProperty !== ''
137
+ ? `/${parentPage.entitySet}/${navigationProperty}`
138
+ : `/${parentPage.entitySet}`;
139
+ }
140
+ else {
141
+ contextPath =
142
+ navigationProperty && navigationProperty !== ''
143
+ ? `/${convertedParentPattern}/${navigationProperty}`
144
+ : `/${convertedParentPattern}`;
145
+ }
146
+ return contextPath;
147
+ }
148
+ /**
149
+ * Creates an error response object.
150
+ *
151
+ * @param message - The error message.
152
+ * @returns An ExecuteFunctionalityOutput object with error details.
153
+ */
154
+ createErrorResponse(message) {
155
+ return {
156
+ functionalityId: constant_1.ADD_PAGE,
157
+ status: 'Failed',
158
+ message,
159
+ parameters: [],
160
+ appPath: this.params.appPath,
161
+ changes: [],
162
+ timestamp: new Date().toISOString()
163
+ };
164
+ }
165
+ /**
166
+ * Validates the input for page creation.
167
+ *
168
+ * @param newPage - The new page details.
169
+ * @param pages - Existing pages in the application.
170
+ * @param viewName - Optional view name for custom pages.
171
+ * @returns A promise that resolves to an ExecuteFunctionalityOutput object if there's an error, or null if validation passes.
172
+ */
173
+ async validatePageCreationInput(newPage, pages, viewName) {
174
+ if (pages.length && !newPage.parent) {
175
+ return this.createErrorResponse('Provide "parentPage" for page creation');
176
+ }
177
+ if (newPage.pageType === src_1.PageTypeV4.CustomPage) {
178
+ const validationError = this.validateCustomPageViewName(viewName);
179
+ if (validationError) {
180
+ return validationError;
181
+ }
182
+ }
183
+ return null;
184
+ }
185
+ /**
186
+ * Validates the view name for a custom page.
187
+ *
188
+ * @param viewName - The view name to validate.
189
+ * @returns An ExecuteFunctionalityOutput object if there's an error, or null if validation passes.
190
+ */
191
+ validateCustomPageViewName(viewName) {
192
+ const standardEnding = '.view.xml';
193
+ const cleanViewName = viewName?.endsWith(standardEnding) ? viewName.slice(0, -standardEnding.length) : viewName;
194
+ if (!cleanViewName) {
195
+ return this.createErrorResponse('Provide "viewName" for CustomPage');
196
+ }
197
+ if (!/^[a-z][a-z0-9_-]*$/i.exec(cleanViewName)) {
198
+ return this.createErrorResponse(`'viewName' must not contain spaces, must start with an upper-case letter, and may contain letters, digits, and underscores.`);
199
+ }
200
+ return null;
201
+ }
202
+ /**
203
+ * Validates the navigation input for page creation.
204
+ *
205
+ * @param parentPage - The parent page definition, if any.
206
+ * @param navigation - Optional navigation string.
207
+ * @param entitySet - Optional entity set.
208
+ * @param pages - Array of existing page definitions.
209
+ * @returns A promise that resolves to an ExecuteFunctionalityOutput object if there's an error, or null if validation passes.
210
+ */
211
+ async validateNavigationInput(parentPage, navigation, entitySet, pages = []) {
212
+ if (pages.length && !parentPage) {
213
+ return this.createErrorResponse('provided "parentPage" was not found');
214
+ }
215
+ const navigations = await this.getAllowedNavigations(parentPage);
216
+ if (parentPage && !navigations.length) {
217
+ const message = this.getMissingNavigationMessage(types_1.MissingNavigationReason.NoAnyNavigationsForParent, navigations);
218
+ return this.createErrorResponse(message);
219
+ }
220
+ const targetNavigation = this.findTargetNavigation(navigations, navigation, entitySet);
221
+ if (parentPage && !targetNavigation) {
222
+ const message = this.getMissingNavigationMessage(types_1.MissingNavigationReason.NotFoundNavigationForParent, navigations, parentPage.pageId);
223
+ return this.createErrorResponse(message);
224
+ }
225
+ if (!parentPage && !entitySet) {
226
+ const message = this.getMissingNavigationMessage(types_1.MissingNavigationReason.NoEntityProvided, navigations);
227
+ return this.createErrorResponse(message);
228
+ }
229
+ if (!parentPage && !targetNavigation) {
230
+ const message = this.getMissingNavigationMessage(types_1.MissingNavigationReason.NotFoundEntity, navigations);
231
+ return this.createErrorResponse(message);
232
+ }
233
+ return null;
234
+ }
235
+ /**
236
+ * Finds the target navigation option based on the provided navigation or entity set.
237
+ *
238
+ * @param navigations - Array of allowed navigation options.
239
+ * @param navigation - Optional navigation string to search for.
240
+ * @param entitySet - Optional entity set to search for.
241
+ * @returns The matching AllowedNavigationOptions object or undefined if not found.
242
+ */
243
+ findTargetNavigation(navigations, navigation, entitySet) {
244
+ return navigations.find((checkNavigation) => {
245
+ const name = checkNavigation.name.toLocaleLowerCase();
246
+ return name === navigation?.toLocaleLowerCase() || name === entitySet?.toLocaleLowerCase();
247
+ });
248
+ }
249
+ /**
250
+ * Generates a new page using the FPM writer.
251
+ *
252
+ * @param newPage - The new page details.
253
+ * @param parentPage - The parent page, if any.
254
+ * @param targetNavigation - The target navigation option.
255
+ * @param pages - Existing pages in the application.
256
+ * @param viewName - Optional view name for custom pages.
257
+ * @param entitySet - Optional entity set for the new page.
258
+ * @returns A promise that resolves to an object containing the new page ID and changes made.
259
+ */
260
+ async generatePageWithFPMWriter(newPage, parentPage, targetNavigation, pages, viewName, entitySet) {
261
+ const fpnNavigation = parentPage
262
+ ? {
263
+ sourceEntity: parentPage.entitySet,
264
+ sourcePage: parentPage.pageId,
265
+ navEntity: targetNavigation?.name,
266
+ navKey: true
267
+ }
268
+ : undefined;
269
+ const contextPath = this.calculateContextPath(parentPage, targetNavigation?.name, targetNavigation?.entitySet ?? entitySet);
270
+ const id = (0, utils_1.generatePageId)({
271
+ entitySet: targetNavigation?.entitySet ?? entitySet ?? '',
272
+ pageId: '',
273
+ pageType: newPage.pageType,
274
+ contextPath: contextPath,
275
+ viewName
276
+ }, parentPage?.pageId, pages, targetNavigation?.name);
277
+ const pageApi = {
278
+ id,
279
+ entity: targetNavigation?.entitySet ?? entitySet ?? '',
280
+ navigation: fpnNavigation,
281
+ contextPath
282
+ };
283
+ const changes = await this.writeFPM(newPage.pageType, pageApi, viewName);
284
+ return { pageID: id, changes };
285
+ }
286
+ /**
287
+ * Writes the Flexible Programming Model (FPM) configuration.
288
+ *
289
+ * @param pageType - The type of the page.
290
+ * @param fpmData - The page API configuration.
291
+ * @param viewName - Optional view name for custom pages.
292
+ * @returns A promise that resolves to an array of strings representing the changes made.
293
+ */
294
+ async writeFPM(pageType, fpmData, viewName) {
295
+ const ftfsFileIo = new page_editor_api_1.SapuxFtfsFileIO(this.applicationAccess);
296
+ let customExtension = src_1.CustomExtensionType.ListReport;
297
+ if (pageType === src_1.PageTypeV4.ObjectPage) {
298
+ customExtension = src_1.CustomExtensionType.ObjectPage;
299
+ }
300
+ if (pageType === src_1.PageTypeV4.CustomPage && viewName) {
301
+ customExtension = src_1.CustomExtensionType.CustomPage;
302
+ }
303
+ const folder = (0, utils_2.getDefaultExtensionFolder)(project_access_1.DirName.View);
304
+ return ftfsFileIo.writeFPM({
305
+ customExtension,
306
+ basePath: (0, path_1.join)(this.applicationAccess.project.root, this.appId),
307
+ data: { ...fpmData, folder }
308
+ });
309
+ }
310
+ /**
311
+ * Deletes navigation links for a given page.
312
+ *
313
+ * @param navigation - The navigation object containing links.
314
+ * @param pageName - The name of the page to remove links for.
315
+ */
316
+ deleteNavigationLinks(navigation, pageName) {
317
+ if (navigation) {
318
+ for (const key in navigation) {
319
+ const navigationPage = this.findPageByNavigation(navigation, key);
320
+ if (navigationPage === pageName) {
321
+ delete navigation[key];
322
+ break;
323
+ }
324
+ }
325
+ }
326
+ }
327
+ /**
328
+ * Function to evaluate the navigation entry of a page.
329
+ * Old format (spec version <=3): <target page name>: <route name>.
330
+ * New format (spec version >=4): <route name>: { route: <target page name>}.
331
+ *
332
+ * @param pageObjNavigation - Page data.
333
+ * @param key - key in schema for the route definition.
334
+ * @returns the target page name from the route definition.
335
+ */
336
+ findPageByNavigation(pageObjNavigation, key) {
337
+ const navigationEntry = pageObjNavigation?.[key];
338
+ return typeof navigationEntry === 'string' ? key : navigationEntry?.route;
339
+ }
340
+ /**
341
+ * Method which deletes pages recursively by looping through navigation.
342
+ *
343
+ * @param app - Content of 'app.json'.
344
+ * @param pageName - page's name to remove
345
+ */
346
+ deletePageRecursively(app, pageName) {
347
+ if (!pageName) {
348
+ return;
349
+ }
350
+ const { pages } = app;
351
+ const settings = app.settings ?? {};
352
+ if (pages?.[pageName]) {
353
+ const page = pages?.[pageName];
354
+ delete pages[pageName];
355
+ // Delete controller extension of page
356
+ const extensionId = `${page.pageType}#${pageName}`;
357
+ if (settings.controllerExtensions?.[extensionId]) {
358
+ delete settings.controllerExtensions[extensionId];
359
+ }
360
+ // Delete child pages
361
+ if (page.navigation) {
362
+ for (const key in page.navigation) {
363
+ const subPage = this.findPageByNavigation(page.navigation, key);
364
+ this.deletePageRecursively(app, subPage);
365
+ }
366
+ }
367
+ }
368
+ }
369
+ /**
370
+ * Retrieves all pages defined in the application.
371
+ *
372
+ * @returns An array of PageDef objects representing the pages in the application.
373
+ */
374
+ getPages() {
375
+ const { config } = this.appData;
376
+ const { pages = {} } = config;
377
+ const result = [];
378
+ for (const pageId in pages) {
379
+ const page = pages[pageId];
380
+ result.push({
381
+ pageId,
382
+ pageType: page.pageType ?? src_1.PageTypeV4.ListReport,
383
+ entitySet: page.entitySet ?? '',
384
+ contextPath: page.contextPath,
385
+ routePattern: 'routePattern' in page && typeof page.routePattern === 'string' ? page.routePattern : undefined
386
+ });
387
+ }
388
+ return result;
389
+ }
390
+ /**
391
+ * Retrieves allowed navigation options for a given parent page.
392
+ *
393
+ * @param parentPage - Optional parent page definition.
394
+ * @param refresh - Whether to refresh the data from the service.
395
+ * @returns A promise that resolves to an array of AllowedNavigationOptions.
396
+ */
397
+ async getAllowedNavigations(parentPage, refresh = false) {
398
+ if (!parentPage || parentPage?.pageType === 'ListReport') {
399
+ // Entities from zero page or from list report
400
+ return this.getRootPageNavigationOptions(refresh);
401
+ }
402
+ // Navigation from object page or custom page
403
+ const service = await (0, serviceStore_1.getService)({
404
+ appName: this.appId,
405
+ project: this.applicationAccess.project,
406
+ serviceName: this.serviceName
407
+ });
408
+ return (await service.getAllowedNavigations(parentPage?.entitySet, parentPage?.entitySet, refresh)).map((navigation) => ({ ...navigation, isNavigation: true }));
409
+ }
410
+ /**
411
+ * Retrieves navigation options for the root page.
412
+ *
413
+ * @param refresh - Whether to refresh the data from the service.
414
+ * @returns A promise that resolves to an array of AllowedNavigationOptions.
415
+ */
416
+ async getRootPageNavigationOptions(refresh = false) {
417
+ const service = await (0, serviceStore_1.getService)({
418
+ appName: this.appId,
419
+ project: this.applicationAccess.project,
420
+ serviceName: this.serviceName
421
+ });
422
+ const entities = await service.getAllowedEntities(refresh);
423
+ return entities.map((entity) => {
424
+ return {
425
+ entitySet: entity.entitySet,
426
+ name: entity.entitySet
427
+ };
428
+ });
429
+ }
430
+ /**
431
+ * Retrieves creation options for a new page.
432
+ *
433
+ * @param pageType - Optional page type to get specific creation options.
434
+ * @returns A promise that resolves to GetFunctionalityDetailsOutput containing creation options.
435
+ */
436
+ async getCreationOptions(pageType) {
437
+ exports.ADD_PAGE_FUNCTIONALITY.parameters = [];
438
+ const pages = this.getPages();
439
+ if (pages.length) {
440
+ // Validate navigation
441
+ const navigationsMap = {};
442
+ let refreshNavigations = true;
443
+ for (const page of pages) {
444
+ const navigations = await this.getAllowedNavigations(page, refreshNavigations);
445
+ navigationsMap[page.pageId] = this.getAllowedNavigationsOutput(navigations);
446
+ // Pass with refresh only once
447
+ refreshNavigations = false;
448
+ }
449
+ if (pageType === src_1.PageTypeV4.CustomPage) {
450
+ exports.ADD_PAGE_FUNCTIONALITY.parameters.push({
451
+ id: 'pageViewName',
452
+ type: 'string',
453
+ description: `Name of custom view file. First try to extract view name from user input that satisfies the pattern, if not possible ask user to provide view name`,
454
+ pattern: '/^[a-zA-Z][a-zA-Z0-9_-]{0,}$/i',
455
+ required: true
456
+ });
457
+ }
458
+ exports.ADD_PAGE_FUNCTIONALITY.parameters = [
459
+ {
460
+ id: 'parentPage',
461
+ type: 'string',
462
+ description: `Parent page is id/name of parent page. First try to extract parent page from user input in a format defined in example, if not possible suggest content defined in options`,
463
+ options: Object.keys(navigationsMap),
464
+ required: true,
465
+ examples: ['parentPage: ' + Object.keys(navigationsMap)[0]]
466
+ },
467
+ {
468
+ id: 'pageNavigation',
469
+ type: 'string',
470
+ description: `Page navigation option for parent page. First try to extract navigation option from user input in a format defined in example, if not possible suggest content defined in options`,
471
+ options: Object.keys(navigationsMap).map((item) => `for ${item}: available navigation(s) is/are one of: ${navigationsMap[item]}`),
472
+ examples: ['pageNavigation: ' + Object.values(navigationsMap)[0].split(',')[0]],
473
+ required: true
474
+ },
475
+ {
476
+ id: 'pageType',
477
+ type: 'string',
478
+ description: `Type of page to be created. First try to extract page type from user input in a format defined in example, if not possible suggest content defined in options.`,
479
+ options: Object.keys(src_1.PageTypeV4),
480
+ examples: ['pageType: ' + Object.keys(src_1.PageTypeV4)[0]],
481
+ required: true
482
+ }
483
+ ];
484
+ return exports.ADD_PAGE_FUNCTIONALITY;
485
+ }
486
+ // Creation of very first page
487
+ const entities = await this.getAllowedNavigations();
488
+ exports.ADD_PAGE_FUNCTIONALITY.parameters = [
489
+ {
490
+ id: 'entitySet',
491
+ type: 'string',
492
+ description: `Entity set for the new page. First try to extract entity from user input in a format defined in example, if not possible suggest content defined in options.`,
493
+ options: entities.map((entity) => this.getAllowedNavigationsName(entity)),
494
+ examples: ['entitySet: ' + entities.map((entity) => this.getAllowedNavigationsName(entity))[0]],
495
+ required: true
496
+ },
497
+ {
498
+ id: 'pageType',
499
+ type: 'string',
500
+ description: `Type of page to be created. First try to extract page type from user input in a format defined in example, if not possible suggest content defined in options.`,
501
+ options: Object.keys(src_1.PageTypeV4),
502
+ examples: ['pageType: ' + Object.keys(src_1.PageTypeV4)[0]],
503
+ required: true
504
+ }
505
+ ];
506
+ return exports.ADD_PAGE_FUNCTIONALITY;
507
+ }
508
+ /**
509
+ * Creates a new page in the application.
510
+ *
511
+ * @param newPage - Details of the new page to be created.
512
+ * @returns A promise that resolves to ExecuteFunctionalityOutput containing the result of the page creation.
513
+ */
514
+ async createPage(newPage) {
515
+ const { parent, navigation, pageType, entitySet } = newPage;
516
+ const viewName = newPage.pageType === src_1.PageTypeV4.CustomPage ? newPage.viewName : undefined;
517
+ const pages = this.getPages();
518
+ // Validate input parameters
519
+ const validationError = await this.validatePageCreationInput(newPage, pages, viewName);
520
+ if (validationError) {
521
+ return validationError;
522
+ }
523
+ // Find parent page and validate navigation
524
+ const parentPage = pages.find((page) => page.pageId === parent);
525
+ const navigationValidationError = await this.validateNavigationInput(parentPage, navigation, entitySet, pages);
526
+ if (navigationValidationError) {
527
+ return navigationValidationError;
528
+ }
529
+ // Generate the page
530
+ const navigations = await this.getAllowedNavigations(parentPage);
531
+ const targetNavigation = this.findTargetNavigation(navigations, navigation, entitySet);
532
+ const { pageID, changes } = await this.generatePageWithFPMWriter(newPage, parentPage, targetNavigation, pages, viewName, entitySet);
533
+ return {
534
+ functionalityId: constant_1.ADD_PAGE,
535
+ status: changes.length ? 'success' : 'skipped',
536
+ message: changes.length
537
+ ? `Page with id '${pageID}' of type '${pageType}' was created successfully in application '${this.appId}'`
538
+ : `No changes were made for '${pageType}'`,
539
+ parameters: [],
540
+ appPath: this.params.appPath,
541
+ changes,
542
+ timestamp: new Date().toISOString()
543
+ };
544
+ }
545
+ /**
546
+ * Retrieves options for deleting a page.
547
+ *
548
+ * @returns A promise that resolves to GetFunctionalityDetailsOutput containing delete options.
549
+ */
550
+ async getDeleteOptions() {
551
+ exports.DELETE_PAGE_FUNCTIONALITY.parameters = [];
552
+ const pages = this.getPages();
553
+ exports.DELETE_PAGE_FUNCTIONALITY.parameters.push({
554
+ id: 'pageId',
555
+ type: 'string',
556
+ description: `Page id to be deleted. First try to extract page id from user input in a format defined in example, if not possible suggest content defined in options`,
557
+ options: pages.map((page) => page.pageId),
558
+ examples: ['pageId: ' + pages.map((page) => page.pageId)[0]],
559
+ required: true
560
+ });
561
+ return exports.DELETE_PAGE_FUNCTIONALITY;
562
+ }
563
+ /**
564
+ * Deletes a page from the application.
565
+ *
566
+ * @param page - The page to be deleted.
567
+ * @param page.pageId - The ID of the page to be deleted.
568
+ * @returns A promise that resolves to ExecuteFunctionalityOutput containing the result of the page deletion.
569
+ */
570
+ async deletePage(page) {
571
+ const { pageId } = page;
572
+ const appData = structuredClone(this.appData);
573
+ const { config } = appData;
574
+ let deleteHappened = false;
575
+ const { pages = {} } = config;
576
+ if (pages?.[pageId]) {
577
+ // Remove reference in 'navigation' to page from parent
578
+ for (const parentPage in pages) {
579
+ if (pages[parentPage].navigation) {
580
+ this.deleteNavigationLinks(pages[parentPage].navigation, pageId);
581
+ }
582
+ }
583
+ // Delete page recursively together with all child pages
584
+ this.deletePageRecursively(config, pageId);
585
+ deleteHappened = true;
586
+ }
587
+ // delete home property if last page has been deleted
588
+ if (Object.keys(pages).length === 0) {
589
+ delete config.home;
590
+ }
591
+ if (deleteHappened) {
592
+ const ftfsFileIo = new page_editor_api_1.SapuxFtfsFileIO(this.applicationAccess);
593
+ await ftfsFileIo.writeApp(appData);
594
+ return {
595
+ functionalityId: constant_1.DELETE_PAGE,
596
+ status: 'success',
597
+ message: `Page with id '${pageId}' was deleted successfully in application '${this.appId}'`,
598
+ parameters: [],
599
+ appPath: this.params.appPath,
600
+ changes: [],
601
+ timestamp: new Date().toISOString()
602
+ };
603
+ }
604
+ return {
605
+ functionalityId: constant_1.DELETE_PAGE,
606
+ status: 'unchanged',
607
+ message: `Page with id '${pageId}' was not found in application '${this.appId}'`,
608
+ parameters: [],
609
+ appPath: this.params.appPath,
610
+ changes: [],
611
+ timestamp: new Date().toISOString()
612
+ };
613
+ }
614
+ }
615
+ exports.Application = Application;
616
+ //# sourceMappingURL=application.js.map
@@ -0,0 +1,4 @@
1
+ import type { FunctionalityHandlers } from '../../../types';
2
+ export declare const deletePageHandlers: FunctionalityHandlers;
3
+ export { DELETE_PAGE_FUNCTIONALITY } from './application';
4
+ //# sourceMappingURL=delete-page.d.ts.map
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DELETE_PAGE_FUNCTIONALITY = exports.deletePageHandlers = void 0;
4
+ const application_1 = require("./application");
5
+ const page_editor_api_1 = require("../../../page-editor-api");
6
+ const utils_1 = require("../../utils");
7
+ const constant_1 = require("../../../constant");
8
+ /**
9
+ * Retrieves the details of the Delete Page functionality.
10
+ *
11
+ * @param params - The input parameters for getting functionality details.
12
+ * @returns A promise resolving to the functionality details output.
13
+ */
14
+ async function getFunctionalityDetails(params) {
15
+ const { appPath } = params;
16
+ const appDetails = await (0, utils_1.resolveApplication)(appPath);
17
+ if (!appDetails?.applicationAccess) {
18
+ return {
19
+ functionalityId: constant_1.DELETE_PAGE,
20
+ name: 'Invalid Project Root or Application Path',
21
+ description: `To delete a page, provide a valid project root or application path. "${appPath}" is not valid`,
22
+ parameters: []
23
+ };
24
+ }
25
+ const { appId, applicationAccess } = appDetails;
26
+ const ftfsFileIo = new page_editor_api_1.SapuxFtfsFileIO(applicationAccess);
27
+ const appData = await ftfsFileIo.readApp();
28
+ const serviceName = await (0, page_editor_api_1.getServiceName)(applicationAccess);
29
+ const application = new application_1.Application({ params, applicationAccess, serviceName, appId, appData });
30
+ return application.getDeleteOptions();
31
+ }
32
+ /**
33
+ * Executes the Delete Page functionality.
34
+ *
35
+ * @param params - The input parameters for executing the functionality.
36
+ * @returns A promise resolving to the execution output.
37
+ */
38
+ async function executeFunctionality(params) {
39
+ const { appPath, parameters } = params;
40
+ const { pageId } = parameters;
41
+ if (!pageId || typeof pageId !== 'string') {
42
+ throw new Error('Missing or invalid parameter "pageId"');
43
+ }
44
+ const appDetails = await (0, utils_1.resolveApplication)(appPath);
45
+ if (!appDetails?.applicationAccess) {
46
+ return {
47
+ functionalityId: constant_1.DELETE_PAGE,
48
+ status: 'Failed',
49
+ message: `Project root not found for app path: ${appPath}`,
50
+ parameters: [],
51
+ appPath: appPath,
52
+ changes: [],
53
+ timestamp: new Date().toISOString()
54
+ };
55
+ }
56
+ const { appId, applicationAccess } = appDetails;
57
+ const ftfsFileIo = new page_editor_api_1.SapuxFtfsFileIO(applicationAccess);
58
+ const appData = await ftfsFileIo.readApp();
59
+ const serviceName = await (0, page_editor_api_1.getServiceName)(applicationAccess);
60
+ const application = new application_1.Application({ params, applicationAccess, serviceName, appId, appData });
61
+ return application.deletePage({
62
+ pageId
63
+ });
64
+ }
65
+ exports.deletePageHandlers = {
66
+ getFunctionalityDetails,
67
+ executeFunctionality
68
+ };
69
+ var application_2 = require("./application");
70
+ Object.defineProperty(exports, "DELETE_PAGE_FUNCTIONALITY", { enumerable: true, get: function () { return application_2.DELETE_PAGE_FUNCTIONALITY; } });
71
+ //# sourceMappingURL=delete-page.js.map