@sap-ux/preview-middleware 0.13.18 → 0.13.20

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,19 +1,8 @@
1
1
  /// <reference path="../../../../types/ui5.d.ts" />
2
2
  import type { Logger } from '@sap-ux/logger';
3
3
  import type { ReaderCollection } from '@ui5/fs';
4
- /**
5
- * Structure of a flex change.
6
- */
7
- export interface FlexChange {
8
- [key: string]: string | object | undefined;
9
- changeType: string;
10
- reference: string;
11
- moduleName?: string;
12
- content?: {
13
- codeRef?: string;
14
- fragmentPath?: string;
15
- };
16
- }
4
+ import type { Editor } from 'mem-fs-editor';
5
+ import type { CommonChangeProperties } from '@sap-ux/adp-tooling';
17
6
  /**
18
7
  * Read changes from the file system and return them.
19
8
  *
@@ -21,7 +10,7 @@ export interface FlexChange {
21
10
  * @param logger logger instance
22
11
  * @returns object with the file name as key and the file content as value
23
12
  */
24
- export declare function readChanges(project: ReaderCollection, logger: Logger): Promise<Record<string, FlexChange>>;
13
+ export declare function readChanges(project: ReaderCollection, logger: Logger): Promise<Record<string, CommonChangeProperties>>;
25
14
  /**
26
15
  * Writes flex changes to the user's workspace.
27
16
  *
@@ -29,13 +18,14 @@ export declare function readChanges(project: ReaderCollection, logger: Logger):
29
18
  * @param data.fileName file name that is required for a valid change
30
19
  * @param data.fileType file type that is required
31
20
  * @param webappPath path to the webapp folder
21
+ * @param fs mem-fs editor
32
22
  * @param logger logger instance
33
23
  * @returns object with success flag and optional message
34
24
  */
35
25
  export declare function writeChange(data: object & {
36
26
  fileName?: string;
37
27
  fileType?: string;
38
- }, webappPath: string, logger: Logger): {
28
+ }, webappPath: string, fs: Editor, logger: Logger): {
39
29
  success: boolean;
40
30
  message?: string;
41
31
  };
package/dist/base/flex.js CHANGED
@@ -12,28 +12,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.deleteChange = exports.writeChange = exports.readChanges = void 0;
13
13
  const fs_1 = require("fs");
14
14
  const path_1 = require("path");
15
- /**
16
- * Map of change type specific correction functions.
17
- */
18
- const moduleNameContentMap = {
19
- codeExt: (change) => { var _a, _b; return ((_b = (_a = change.content) === null || _a === void 0 ? void 0 : _a.codeRef) !== null && _b !== void 0 ? _b : '').replace('.js', ''); },
20
- addXML: (change) => { var _a, _b; return (_b = (_a = change.content) === null || _a === void 0 ? void 0 : _a.fragmentPath) !== null && _b !== void 0 ? _b : ''; }
21
- };
22
- /**
23
- * Sets the moduleName property of the provided change to also support old changes with newer UI5 versions.
24
- *
25
- * @param change change to be fixed
26
- * @param logger logger instance
27
- */
28
- function tryFixChange(change, logger) {
29
- try {
30
- const prefix = change.reference.replace(/\./g, '/');
31
- change.moduleName = `${prefix}/changes/${moduleNameContentMap[change.changeType](change)}`;
32
- }
33
- catch (error) {
34
- logger.warn('Could not fix missing module name.');
35
- }
36
- }
37
15
  /**
38
16
  * Read changes from the file system and return them.
39
17
  *
@@ -48,9 +26,6 @@ function readChanges(project, logger) {
48
26
  for (const file of files) {
49
27
  try {
50
28
  const change = JSON.parse(yield file.getString());
51
- if (moduleNameContentMap[change.changeType] && !change.moduleName) {
52
- tryFixChange(change, logger);
53
- }
54
29
  changes[`sap.ui.fl.${(0, path_1.parse)(file.getName()).name}`] = change;
55
30
  logger.debug(`Read change from ${file.getPath()}`);
56
31
  }
@@ -69,20 +44,17 @@ exports.readChanges = readChanges;
69
44
  * @param data.fileName file name that is required for a valid change
70
45
  * @param data.fileType file type that is required
71
46
  * @param webappPath path to the webapp folder
47
+ * @param fs mem-fs editor
72
48
  * @param logger logger instance
73
49
  * @returns object with success flag and optional message
74
50
  */
75
- function writeChange(data, webappPath, logger) {
51
+ function writeChange(data, webappPath, fs, logger) {
76
52
  const fileName = data.fileName;
77
53
  const fileType = data.fileType;
78
54
  if (fileName && fileType) {
79
55
  logger.debug(`Write change ${fileName}.${fileType}`);
80
- const path = (0, path_1.join)(webappPath, 'changes');
81
- if (!(0, fs_1.existsSync)(path)) {
82
- (0, fs_1.mkdirSync)(path);
83
- }
84
- const filePath = (0, path_1.join)(path, fileName + '.' + fileType);
85
- (0, fs_1.writeFileSync)(filePath, JSON.stringify(data, null, 2));
56
+ const filePath = (0, path_1.join)(webappPath, 'changes', fileName + '.' + fileType);
57
+ fs.writeJSON(filePath, data);
86
58
  const message = `FILE_CREATED ${fileName}.${fileType}`;
87
59
  return { success: true, message };
88
60
  }
@@ -1,11 +1,12 @@
1
1
  /// <reference path="../../../../types/ui5.d.ts" />
2
2
  import type { ReaderCollection } from '@ui5/fs';
3
+ import type { Editor as MemFsEditor } from 'mem-fs-editor';
3
4
  import type { Router } from 'express';
4
- import type { App, FlpConfig, MiddlewareConfig, RtaConfig, TestConfig } from '../types';
5
5
  import type { Logger, ToolsLogger } from '@sap-ux/logger';
6
6
  import type { MiddlewareUtils } from '@ui5/server';
7
7
  import type { Manifest, UI5FlexLayer } from '@sap-ux/project-access';
8
- import { type AdpPreviewConfig } from '@sap-ux/adp-tooling';
8
+ import { type AdpPreviewConfig, type CommonChangeProperties, type OperationType } from '@sap-ux/adp-tooling';
9
+ import type { App, FlpConfig, MiddlewareConfig, RtaConfig, TestConfig } from '../types';
9
10
  /**
10
11
  * Enhanced request handler that exposes a list of endpoints for the cds-plugin-ui5.
11
12
  */
@@ -53,6 +54,7 @@ export interface TemplateConfig {
53
54
  };
54
55
  locateReuseLibsScript?: boolean;
55
56
  }
57
+ type OnChangeRequestHandler = (type: OperationType, change: CommonChangeProperties, fs: MemFsEditor, logger: Logger) => Promise<void>;
56
58
  /**
57
59
  * Class handling preview of a sandbox FLP.
58
60
  */
@@ -60,6 +62,7 @@ export declare class FlpSandbox {
60
62
  private readonly project;
61
63
  private readonly utils;
62
64
  private readonly logger;
65
+ protected onChangeRequest: OnChangeRequestHandler | undefined;
63
66
  protected templateConfig: TemplateConfig;
64
67
  readonly config: FlpConfig;
65
68
  readonly rta?: RtaConfig;
@@ -74,6 +77,12 @@ export declare class FlpSandbox {
74
77
  * @param logger logger instance
75
78
  */
76
79
  constructor(config: Partial<MiddlewareConfig>, project: ReaderCollection, utils: MiddlewareUtils, logger: Logger);
80
+ /**
81
+ * Registers a handler function to be called when a change request occurs.
82
+ *
83
+ * @param {OnChangeRequestHandler} handler - The function to be executed when a change request occurs.
84
+ */
85
+ addOnChangeRequestHandler(handler: OnChangeRequestHandler): void;
77
86
  /**
78
87
  * Initialize the FLP sandbox router.
79
88
  *
@@ -173,4 +182,5 @@ export declare class FlpSandbox {
173
182
  * @param logger logger instance
174
183
  */
175
184
  export declare function initAdp(rootProject: ReaderCollection, config: AdpPreviewConfig, flp: FlpSandbox, util: MiddlewareUtils, logger: ToolsLogger): Promise<void>;
185
+ export {};
176
186
  //# sourceMappingURL=flp.d.ts.map
package/dist/base/flp.js CHANGED
@@ -10,13 +10,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.initAdp = exports.FlpSandbox = void 0;
13
+ const mem_fs_1 = require("mem-fs");
14
+ const mem_fs_editor_1 = require("mem-fs-editor");
13
15
  const ejs_1 = require("ejs");
14
16
  const fs_1 = require("fs");
15
17
  const path_1 = require("path");
16
18
  const express_1 = require("express");
17
- const flex_1 = require("./flex");
18
- const project_access_1 = require("@sap-ux/project-access");
19
19
  const adp_tooling_1 = require("@sap-ux/adp-tooling");
20
+ const project_access_1 = require("@sap-ux/project-access");
21
+ const flex_1 = require("./flex");
20
22
  const test_1 = require("./test");
21
23
  const DEVELOPER_MODE_CONFIG = new Map([
22
24
  // Run application in design time mode
@@ -114,6 +116,14 @@ class FlpSandbox {
114
116
  logger.debug(`Config: ${JSON.stringify({ flp: this.config, rta: this.rta, test: this.test })}`);
115
117
  this.router = (0, express_1.Router)();
116
118
  }
119
+ /**
120
+ * Registers a handler function to be called when a change request occurs.
121
+ *
122
+ * @param {OnChangeRequestHandler} handler - The function to be executed when a change request occurs.
123
+ */
124
+ addOnChangeRequestHandler(handler) {
125
+ this.onChangeRequest = handler;
126
+ }
117
127
  /**
118
128
  * Initialize the FLP sandbox router.
119
129
  *
@@ -321,16 +331,27 @@ class FlpSandbox {
321
331
  * Create required routes for flex.
322
332
  */
323
333
  createFlexHandler() {
334
+ const fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
324
335
  const api = `${PREVIEW_URL.api}/changes`;
325
336
  this.router.use(api, (0, express_1.json)());
326
337
  this.router.get(api, ((_req, res) => __awaiter(this, void 0, void 0, function* () {
327
- this.sendResponse(res, 'application/json', 200, JSON.stringify(yield (0, flex_1.readChanges)(this.project, this.logger)));
338
+ const changes = yield (0, flex_1.readChanges)(this.project, this.logger);
339
+ if (this.onChangeRequest) {
340
+ for (const change of Object.values(changes)) {
341
+ yield this.onChangeRequest('read', change, fs, this.logger);
342
+ }
343
+ }
344
+ res.status(200).contentType('application/json').send(changes);
328
345
  })));
329
346
  this.router.post(api, ((req, res) => __awaiter(this, void 0, void 0, function* () {
330
347
  try {
331
- const { success, message } = (0, flex_1.writeChange)(req.body, this.utils.getProject().getSourcePath(), this.logger);
348
+ const change = req.body;
349
+ if (this.onChangeRequest) {
350
+ yield this.onChangeRequest('write', change, fs, this.logger);
351
+ }
352
+ const { success, message } = (0, flex_1.writeChange)(change, this.utils.getProject().getSourcePath(), fs, this.logger);
332
353
  if (success) {
333
- this.sendResponse(res, 'text/plain', 200, message !== null && message !== void 0 ? message : '');
354
+ fs.commit(() => res.status(200).send(message));
334
355
  }
335
356
  else {
336
357
  this.sendResponse(res, 'text/plain', 400, 'INVALID_DATA');
@@ -565,6 +586,7 @@ function initAdp(rootProject, config, flp, util, logger) {
565
586
  const { name, manifest } = descriptor;
566
587
  yield flp.init(manifest, name, adp.resources);
567
588
  flp.router.use(adp.descriptor.url, adp.proxy.bind(adp));
589
+ flp.addOnChangeRequestHandler(adp.onChangeRequest.bind(adp));
568
590
  flp.router.use((0, express_1.json)());
569
591
  adp.addApis(flp.router);
570
592
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "sap/ui/dt/OverlayRegistry", "../control-utils", "../command-executor", "../api-handler", "./BaseDialog.controller"], function (MessageToast, JSONModel, OverlayRegistry, __ControlUtils, __CommandExecutor, ___api_handler, __BaseDialog) {
3
+ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "sap/ui/dt/OverlayRegistry", "../control-utils", "../command-executor", "../api-handler", "./BaseDialog.controller", "../utils"], function (MessageToast, JSONModel, OverlayRegistry, __ControlUtils, __CommandExecutor, ___api_handler, __BaseDialog, ___utils) {
4
4
  "use strict";
5
5
 
6
6
  function _interopRequireDefault(obj) {
@@ -9,8 +9,8 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "sap/ui/dt/O
9
9
  const ControlUtils = _interopRequireDefault(__ControlUtils);
10
10
  const CommandExecutor = _interopRequireDefault(__CommandExecutor);
11
11
  const getFragments = ___api_handler["getFragments"];
12
- const writeFragment = ___api_handler["writeFragment"];
13
12
  const BaseDialog = _interopRequireDefault(__BaseDialog);
13
+ const notifyUser = ___utils["notifyUser"];
14
14
  /**
15
15
  * @namespace open.ux.preview.client.adp.controllers
16
16
  */
@@ -74,7 +74,8 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "sap/ui/dt/O
74
74
  fragmentName,
75
75
  targetAggregation
76
76
  };
77
- await this.createNewFragment(fragmentData);
77
+ await this.createFragmentChange(fragmentData);
78
+ notifyUser(`Note: The '${fragmentName}.fragment.xml' fragment will be created once you save the change.`, 8000);
78
79
  this.handleDialogClose();
79
80
  },
80
81
  buildDialogData: async function _buildDialogData() {
@@ -162,28 +163,6 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "sap/ui/dt/O
162
163
  }
163
164
  return indexArray;
164
165
  },
165
- createNewFragment: async function _createNewFragment(fragmentData) {
166
- const {
167
- fragmentName,
168
- index,
169
- targetAggregation
170
- } = fragmentData;
171
- try {
172
- await writeFragment({
173
- fragmentName
174
- });
175
- MessageToast.show(`Fragment with name '${fragmentName}' was created.`);
176
- } catch (e) {
177
- // In case of error when creating a new fragment, we should not create a change file
178
- MessageToast.show(e.message);
179
- throw new Error(e.message);
180
- }
181
- await this.createFragmentChange({
182
- fragmentName,
183
- index,
184
- targetAggregation
185
- });
186
- },
187
166
  createFragmentChange: async function _createFragmentChange(fragmentData) {
188
167
  const {
189
168
  fragmentName,
@@ -23,8 +23,9 @@ import type ElementOverlay from 'sap/ui/dt/ElementOverlay';
23
23
 
24
24
  import ControlUtils from '../control-utils';
25
25
  import CommandExecutor from '../command-executor';
26
- import { getFragments, writeFragment } from '../api-handler';
26
+ import { getFragments } from '../api-handler';
27
27
  import BaseDialog from './BaseDialog.controller';
28
+ import { notifyUser } from '../utils';
28
29
 
29
30
  interface CreateFragmentProps {
30
31
  fragmentName: string;
@@ -142,7 +143,9 @@ export default class AddFragment extends BaseDialog {
142
143
  targetAggregation
143
144
  };
144
145
 
145
- await this.createNewFragment(fragmentData);
146
+ await this.createFragmentChange(fragmentData);
147
+
148
+ notifyUser(`Note: The '${fragmentName}.fragment.xml' fragment will be created once you save the change.`, 8000);
146
149
 
147
150
  this.handleDialogClose();
148
151
  }
@@ -245,28 +248,6 @@ export default class AddFragment extends BaseDialog {
245
248
  return indexArray;
246
249
  }
247
250
 
248
- /**
249
- * Creates a new fragment for the specified control
250
- *
251
- * @param fragmentData Fragment Data
252
- * @param fragmentData.index Index for XML Fragment placement
253
- * @param fragmentData.fragmentName Fragment name
254
- * @param fragmentData.targetAggregation Target aggregation for control
255
- */
256
- private async createNewFragment(fragmentData: CreateFragmentProps): Promise<void> {
257
- const { fragmentName, index, targetAggregation } = fragmentData;
258
- try {
259
- await writeFragment<unknown>({ fragmentName });
260
- MessageToast.show(`Fragment with name '${fragmentName}' was created.`);
261
- } catch (e) {
262
- // In case of error when creating a new fragment, we should not create a change file
263
- MessageToast.show(e.message);
264
- throw new Error(e.message);
265
- }
266
-
267
- await this.createFragmentChange({ fragmentName, index, targetAggregation });
268
- }
269
-
270
251
  /**
271
252
  * Creates an addXML fragment command and pushes it to the command stack
272
253
  *
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
 
3
- sap.ui.define(["sap/ui/core/library", "sap/ui/core/mvc/Controller"], function (sap_ui_core_library, Controller) {
3
+ sap.ui.define(["sap/ui/core/library", "sap/ui/core/mvc/Controller", "../utils"], function (sap_ui_core_library, Controller, ___utils) {
4
4
  "use strict";
5
5
 
6
6
  const ValueState = sap_ui_core_library["ValueState"];
7
+ const matchesFragmentName = ___utils["matchesFragmentName"];
7
8
  /**
8
9
  * @namespace open.ux.preview.client.adp.controllers
9
10
  */
@@ -37,9 +38,25 @@ sap.ui.define(["sap/ui/core/library", "sap/ui/core/mvc/Controller"], function (s
37
38
  updateDialogState(ValueState.Error, 'A fragment file name cannot contain more than 64 characters.');
38
39
  return;
39
40
  }
41
+ const changeExists = this.checkForExistingChange(fragmentName);
42
+ if (changeExists) {
43
+ updateDialogState(ValueState.Error, 'Enter a different name. The fragment name entered matches the name of an unsaved fragment.');
44
+ return;
45
+ }
40
46
  updateDialogState(ValueState.Success);
41
47
  this.model.setProperty('/newFragmentName', fragmentName);
42
48
  },
49
+ checkForExistingChange: function _checkForExistingChange(fragmentName) {
50
+ const allCommands = this.rta.getCommandStack().getCommands();
51
+ return allCommands.some(command => {
52
+ if (command?.getProperty('name') === 'composite') {
53
+ const addXmlCommand = command.getCommands().find(c => c?.getProperty('name') === 'addXMLAtExtensionPoint');
54
+ return addXmlCommand && matchesFragmentName(addXmlCommand, fragmentName);
55
+ } else {
56
+ return matchesFragmentName(command, fragmentName);
57
+ }
58
+ });
59
+ },
43
60
  setEscapeHandler: function _setEscapeHandler() {
44
61
  this.dialog.setEscapeHandler(_ref => {
45
62
  let {
@@ -7,8 +7,10 @@ import { ValueState } from 'sap/ui/core/library';
7
7
  import Controller from 'sap/ui/core/mvc/Controller';
8
8
  import JSONModel from 'sap/ui/model/json/JSONModel';
9
9
  import RuntimeAuthoring from 'sap/ui/rta/RuntimeAuthoring';
10
+ import FlexCommand from 'sap/ui/rta/command/FlexCommand';
10
11
 
11
12
  import CommandExecutor from '../command-executor';
13
+ import { matchesFragmentName } from '../utils';
12
14
 
13
15
  /**
14
16
  * @namespace open.ux.preview.client.adp.controllers
@@ -45,7 +47,7 @@ export default abstract class BaseDialog extends Controller {
45
47
 
46
48
  abstract setup(dialog: Dialog): Promise<void>;
47
49
 
48
- abstract onCreateBtnPress(event: Event): Promise<void>;
50
+ abstract onCreateBtnPress(event: Event): Promise<void> | void;
49
51
 
50
52
  abstract buildDialogData(): Promise<void>;
51
53
 
@@ -54,7 +56,7 @@ export default abstract class BaseDialog extends Controller {
54
56
  *
55
57
  * @param event Event
56
58
  */
57
- onFragmentNameInputChange(event: Event) {
59
+ onFragmentNameInputChange(event: Event): void {
58
60
  const input = event.getSource<Input>();
59
61
  const beginBtn = this.dialog.getBeginButton();
60
62
 
@@ -94,10 +96,42 @@ export default abstract class BaseDialog extends Controller {
94
96
  return;
95
97
  }
96
98
 
99
+ const changeExists = this.checkForExistingChange(fragmentName);
100
+
101
+ if (changeExists) {
102
+ updateDialogState(
103
+ ValueState.Error,
104
+ 'Enter a different name. The fragment name entered matches the name of an unsaved fragment.'
105
+ );
106
+ return;
107
+ }
108
+
97
109
  updateDialogState(ValueState.Success);
98
110
  this.model.setProperty('/newFragmentName', fragmentName);
99
111
  }
100
112
 
113
+ /**
114
+ * Checks for the existence of a change associated with a specific fragment name in the RTA command stack.
115
+ *
116
+ * @param {string} fragmentName - The name of the fragment to check for existing changes.
117
+ * @returns {Promise<boolean>} A promise that resolves to `true` if a matching change is found, otherwise `false`.
118
+ */
119
+ checkForExistingChange(fragmentName: string): boolean {
120
+ const allCommands = this.rta.getCommandStack().getCommands();
121
+
122
+ return allCommands.some((command: FlexCommand) => {
123
+ if (command?.getProperty('name') === 'composite') {
124
+ const addXmlCommand = command
125
+ .getCommands()
126
+ .find((c: FlexCommand) => c?.getProperty('name') === 'addXMLAtExtensionPoint');
127
+
128
+ return addXmlCommand && matchesFragmentName(addXmlCommand, fragmentName);
129
+ } else {
130
+ return matchesFragmentName(command, fragmentName);
131
+ }
132
+ });
133
+ }
134
+
101
135
  /**
102
136
  * Sets custom function that fires when user presses escape key.
103
137
  */
@@ -1,22 +1,23 @@
1
1
  "use strict";
2
2
 
3
- sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "../api-handler", "./BaseDialog.controller"], function (MessageToast, JSONModel, ___api_handler, __BaseDialog) {
3
+ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "../api-handler", "./BaseDialog.controller", "../utils"], function (MessageToast, JSONModel, ___api_handler, __BaseDialog, ___utils) {
4
4
  "use strict";
5
5
 
6
6
  function _interopRequireDefault(obj) {
7
7
  return obj && obj.__esModule && typeof obj.default !== "undefined" ? obj.default : obj;
8
8
  }
9
9
  const getFragments = ___api_handler["getFragments"];
10
- const writeFragment = ___api_handler["writeFragment"];
11
10
  const BaseDialog = _interopRequireDefault(__BaseDialog);
11
+ const notifyUser = ___utils["notifyUser"];
12
12
  /**
13
13
  * @namespace open.ux.preview.client.adp.controllers
14
14
  */
15
15
  const ExtensionPoint = BaseDialog.extend("open.ux.preview.client.adp.controllers.ExtensionPoint", {
16
- constructor: function _constructor(name, _overlays, _rta, data) {
16
+ constructor: function _constructor(name, _overlays, rta, data) {
17
17
  BaseDialog.prototype.constructor.call(this, name);
18
18
  this.model = new JSONModel();
19
19
  this.data = data;
20
+ this.rta = rta;
20
21
  },
21
22
  setup: async function _setup(dialog) {
22
23
  this.dialog = dialog;
@@ -25,11 +26,12 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "../api-hand
25
26
  this.dialog.setModel(this.model);
26
27
  this.dialog.open();
27
28
  },
28
- onCreateBtnPress: async function _onCreateBtnPress(event) {
29
+ onCreateBtnPress: function _onCreateBtnPress(event) {
29
30
  const source = event.getSource();
30
31
  source.setEnabled(false);
31
32
  const fragmentName = this.model.getProperty('/newFragmentName');
32
- await this.createNewFragment(fragmentName);
33
+ this.createExtensionPointFragmentChange(fragmentName);
34
+ notifyUser(`Note: The '${fragmentName}.fragment.xml' fragment will be created once you save the change.`, 8000);
33
35
  this.handleDialogClose();
34
36
  },
35
37
  onExtensionPointHandler: function _onExtensionPointHandler(event) {
@@ -75,22 +77,10 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/model/json/JSONModel", "../api-hand
75
77
  this.model.setProperty('/extensionPointList', list);
76
78
  this.model.setProperty('/extensionListEnabled', enabled);
77
79
  },
78
- createNewFragment: async function _createNewFragment(fragmentName) {
79
- try {
80
- await writeFragment({
81
- fragmentName
82
- });
83
- MessageToast.show(`Fragment with name '${fragmentName}' was created.`);
84
- } catch (e) {
85
- // In case of error when creating a new fragment, we should not create a change file
86
- MessageToast.show(e.message);
87
- throw new Error(e.message);
88
- }
89
- this.createExtensionPointFragmentChange(fragmentName);
90
- },
91
80
  createExtensionPointFragmentChange: function _createExtensionPointFragmentChange(fragmentName) {
92
81
  const extensionPointName = this.model.getProperty('/extensionPointName');
93
82
  const modifiedValue = {
83
+ fragment: `<core:FragmentDefinition xmlns:core='sap.ui.core'></core:FragmentDefinition>`,
94
84
  fragmentPath: `fragments/${fragmentName}.fragment.xml`,
95
85
  extensionPointName
96
86
  };
@@ -16,9 +16,10 @@ import JSONModel from 'sap/ui/model/json/JSONModel';
16
16
  /** sap.ui.rta */
17
17
  import type RuntimeAuthoring from 'sap/ui/rta/RuntimeAuthoring';
18
18
 
19
- import { getFragments, writeFragment } from '../api-handler';
19
+ import { getFragments } from '../api-handler';
20
20
  import BaseDialog from './BaseDialog.controller';
21
21
  import { ExtensionPointData, ExtensionPointInfo } from '../extension-point';
22
+ import { notifyUser } from '../utils';
22
23
 
23
24
  /**
24
25
  * @namespace open.ux.preview.client.adp.controllers
@@ -26,10 +27,11 @@ import { ExtensionPointData, ExtensionPointInfo } from '../extension-point';
26
27
  export default class ExtensionPoint extends BaseDialog {
27
28
  public readonly data: ExtensionPointData;
28
29
 
29
- constructor(name: string, _overlays: UI5Element, _rta: RuntimeAuthoring, data: ExtensionPointData) {
30
+ constructor(name: string, _overlays: UI5Element, rta: RuntimeAuthoring, data: ExtensionPointData) {
30
31
  super(name);
31
32
  this.model = new JSONModel();
32
33
  this.data = data;
34
+ this.rta = rta;
33
35
  }
34
36
 
35
37
  /**
@@ -54,13 +56,15 @@ export default class ExtensionPoint extends BaseDialog {
54
56
  *
55
57
  * @param event Event
56
58
  */
57
- async onCreateBtnPress(event: Event): Promise<void> {
59
+ onCreateBtnPress(event: Event): void {
58
60
  const source = event.getSource<Button>();
59
61
  source.setEnabled(false);
60
62
 
61
63
  const fragmentName = this.model.getProperty('/newFragmentName');
62
64
 
63
- await this.createNewFragment(fragmentName);
65
+ this.createExtensionPointFragmentChange(fragmentName);
66
+
67
+ notifyUser(`Note: The '${fragmentName}.fragment.xml' fragment will be created once you save the change.`, 8000);
64
68
 
65
69
  this.handleDialogClose();
66
70
  }
@@ -127,24 +131,6 @@ export default class ExtensionPoint extends BaseDialog {
127
131
  this.model.setProperty('/extensionListEnabled', enabled);
128
132
  }
129
133
 
130
- /**
131
- * Creates a new fragment for the specified extension point
132
- *
133
- * @param fragmentName Fragment name
134
- */
135
- private async createNewFragment(fragmentName: string): Promise<void> {
136
- try {
137
- await writeFragment<unknown>({ fragmentName });
138
- MessageToast.show(`Fragment with name '${fragmentName}' was created.`);
139
- } catch (e) {
140
- // In case of error when creating a new fragment, we should not create a change file
141
- MessageToast.show(e.message);
142
- throw new Error(e.message);
143
- }
144
-
145
- this.createExtensionPointFragmentChange(fragmentName);
146
- }
147
-
148
134
  /**
149
135
  * Creates add xml at extension point changes
150
136
  *
@@ -153,6 +139,7 @@ export default class ExtensionPoint extends BaseDialog {
153
139
  private createExtensionPointFragmentChange(fragmentName: string): void {
154
140
  const extensionPointName = this.model.getProperty('/extensionPointName');
155
141
  const modifiedValue = {
142
+ fragment: `<core:FragmentDefinition xmlns:core='sap.ui.core'></core:FragmentDefinition>`,
156
143
  fragmentPath: `fragments/${fragmentName}.fragment.xml`,
157
144
  extensionPointName
158
145
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- sap.ui.define([], function () {
3
+ sap.ui.define(["sap/m/MessageToast"], function (MessageToast) {
4
4
  "use strict";
5
5
 
6
6
  /**
@@ -26,10 +26,38 @@ sap.ui.define([], function () {
26
26
  reject
27
27
  };
28
28
  }
29
+
30
+ /**
31
+ * Checks if the fragment name associated with a command matches the specified fragment name.
32
+ *
33
+ * @param {FlexCommand} command - The command object containing the prepared change to be examined.
34
+ * @param {string} fragmentName - The name of the fragment to match against the command's change.
35
+ * @returns {boolean} Returns true if the command's change contains a fragment path that matches
36
+ * the specified fragment name; otherwise, returns false.
37
+ */
38
+ function matchesFragmentName(command, fragmentName) {
39
+ const change = command.getPreparedChange().getDefinition();
40
+ return change.content?.fragmentPath?.includes(`${fragmentName}.fragment.xml`) || false;
41
+ }
42
+
43
+ /**
44
+ * Displays a message to the user indicating that an XML fragment will be created upon saving a change.
45
+ *
46
+ * @param {string} message - The message to be shown in the message toast.
47
+ * @param {number} duration - The duration during which message toast will be active.
48
+ */
49
+ function notifyUser(message) {
50
+ let duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5000;
51
+ MessageToast.show(message, {
52
+ duration
53
+ });
54
+ }
29
55
  var __exports = {
30
56
  __esModule: true
31
57
  };
32
58
  __exports.createDeferred = createDeferred;
59
+ __exports.matchesFragmentName = matchesFragmentName;
60
+ __exports.notifyUser = notifyUser;
33
61
  return __exports;
34
62
  });
35
63
  //# sourceMappingURL=utils.js.map
@@ -1,9 +1,18 @@
1
+ import MessageToast from 'sap/m/MessageToast';
2
+ import type FlexCommand from 'sap/ui/rta/command/FlexCommand';
3
+
1
4
  export interface Deferred<T> {
2
5
  promise: Promise<T>;
3
6
  resolve: (value: T | PromiseLike<T>) => void;
4
7
  reject: (reason?: unknown) => void;
5
8
  }
6
9
 
10
+ export interface FragmentChange {
11
+ content: {
12
+ fragmentPath: string;
13
+ };
14
+ }
15
+
7
16
  /**
8
17
  * Defers the resolution of the promise, stores resolve/reject functions so that they can be accessed at a later stage.
9
18
  *
@@ -26,3 +35,28 @@ export function createDeferred<T>(): Deferred<T> {
26
35
 
27
36
  return { promise, resolve, reject };
28
37
  }
38
+
39
+ /**
40
+ * Checks if the fragment name associated with a command matches the specified fragment name.
41
+ *
42
+ * @param {FlexCommand} command - The command object containing the prepared change to be examined.
43
+ * @param {string} fragmentName - The name of the fragment to match against the command's change.
44
+ * @returns {boolean} Returns true if the command's change contains a fragment path that matches
45
+ * the specified fragment name; otherwise, returns false.
46
+ */
47
+ export function matchesFragmentName(command: FlexCommand, fragmentName: string): boolean {
48
+ const change = command.getPreparedChange().getDefinition() as unknown as FragmentChange;
49
+ return change.content?.fragmentPath?.includes(`${fragmentName}.fragment.xml`) || false;
50
+ }
51
+
52
+ /**
53
+ * Displays a message to the user indicating that an XML fragment will be created upon saving a change.
54
+ *
55
+ * @param {string} message - The message to be shown in the message toast.
56
+ * @param {number} duration - The duration during which message toast will be active.
57
+ */
58
+ export function notifyUser(message: string, duration: number = 5000) {
59
+ MessageToast.show(message, {
60
+ duration
61
+ });
62
+ }
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "bugs": {
10
10
  "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Apreview-middleware"
11
11
  },
12
- "version": "0.13.18",
12
+ "version": "0.13.20",
13
13
  "license": "Apache-2.0",
14
14
  "author": "@SAP/ux-tools-team",
15
15
  "main": "dist/index.js",
@@ -23,10 +23,12 @@
23
23
  ],
24
24
  "dependencies": {
25
25
  "ejs": "3.1.9",
26
+ "mem-fs": "2.1.0",
27
+ "mem-fs-editor": "9.4.0",
26
28
  "@sap-ux/logger": "0.5.1",
27
- "@sap-ux/adp-tooling": "0.9.19",
29
+ "@sap-ux/adp-tooling": "0.9.21",
28
30
  "@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@0.4.17",
29
- "@sap-ux/project-access": "1.19.11"
31
+ "@sap-ux/project-access": "1.19.12"
30
32
  },
31
33
  "devDependencies": {
32
34
  "@types/ejs": "3.1.2",
@@ -34,13 +36,15 @@
34
36
  "@types/prompts": "2.4.4",
35
37
  "@types/supertest": "2.0.12",
36
38
  "copyfiles": "2.4.1",
39
+ "@types/mem-fs": "1.1.2",
40
+ "@types/mem-fs-editor": "7.0.1",
37
41
  "express": "4.19.2",
38
42
  "npm-run-all": "4.1.5",
39
43
  "nock": "13.4.0",
40
44
  "supertest": "6.3.3",
41
45
  "@sap-ux-private/playwright": "0.0.3",
42
46
  "dotenv": "16.3.1",
43
- "@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@0.9.5",
47
+ "@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@0.9.6",
44
48
  "@sap-ux/axios-extension": "1.11.7",
45
49
  "@sap-ux/store": "0.5.0",
46
50
  "@sap-ux/i18n": "0.0.6"