@sap/ux-ui5-tooling 1.21.0 → 1.22.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.
@@ -11,6 +11,9 @@ sap.ui.define(["../utils/error"], function (___utils_error) {
11
11
  ApiEndpoints["CODE_EXT"] = "/adp/api/code_ext";
12
12
  ApiEndpoints["ANNOTATION_FILE"] = "/adp/api/annotation";
13
13
  ApiEndpoints["MANIFEST_APP_DESCRIPTOR"] = "/manifest.appdescr_variant";
14
+ ApiEndpoints["OVP_DATA_SOURCES"] = "/adp/api/ovp/datasources";
15
+ ApiEndpoints["OVP_METAMODEL"] = "/adp/api/ovp/metamodel";
16
+ ApiEndpoints["I18N_STORE"] = "/editor/i18n";
14
17
  return ApiEndpoints;
15
18
  }(ApiEndpoints || {});
16
19
  var RequestMethod = /*#__PURE__*/function (RequestMethod) {
@@ -7,7 +7,10 @@ export const enum ApiEndpoints {
7
7
  CONTROLLER = '/adp/api/controller',
8
8
  CODE_EXT = '/adp/api/code_ext',
9
9
  ANNOTATION_FILE = '/adp/api/annotation',
10
- MANIFEST_APP_DESCRIPTOR = '/manifest.appdescr_variant'
10
+ MANIFEST_APP_DESCRIPTOR = '/manifest.appdescr_variant',
11
+ OVP_DATA_SOURCES = '/adp/api/ovp/datasources',
12
+ OVP_METAMODEL = '/adp/api/ovp/metamodel',
13
+ I18N_STORE = '/editor/i18n'
11
14
  }
12
15
 
13
16
  export const enum RequestMethod {
@@ -67,8 +67,17 @@ sap.ui.define(["sap/base/Log", "open/ux/preview/client/thirdparty/@sap-ux-privat
67
67
  const extPointService = new ExtensionPointService(rta);
68
68
  extPointService.init();
69
69
  }
70
- const applicationType = getApplicationType(rta.getRootControlInstance().getManifest());
70
+ const manifest = rta.getRootControlInstance().getManifest();
71
+ const applicationType = getApplicationType(manifest);
71
72
  const quickActionRegistries = await loadDefinitions(applicationType);
73
+
74
+ // Initialize OVP bridge functions if the application is an OVP app
75
+ if (manifest['sap.ovp']) {
76
+ const {
77
+ initOvpWindowFunctions
78
+ } = await __ui5_require_async('open/ux/preview/client/adp/ovp-window-functions');
79
+ initOvpWindowFunctions();
80
+ }
72
81
  await init(rta, quickActionRegistries);
73
82
 
74
83
  // Register synchronious views detection and warning
@@ -50,9 +50,16 @@ export default async function (rta: RuntimeAuthoring) {
50
50
  extPointService.init();
51
51
  }
52
52
 
53
- const applicationType = getApplicationType(rta.getRootControlInstance().getManifest());
53
+ const manifest = rta.getRootControlInstance().getManifest();
54
+ const applicationType = getApplicationType(manifest);
54
55
  const quickActionRegistries = await loadDefinitions(applicationType);
55
56
 
57
+ // Initialize OVP bridge functions if the application is an OVP app
58
+ if (manifest['sap.ovp']) {
59
+ const { initOvpWindowFunctions } = await import('open/ux/preview/client/adp/ovp-window-functions');
60
+ initOvpWindowFunctions();
61
+ }
62
+
56
63
  await init(rta, quickActionRegistries);
57
64
 
58
65
  // Register synchronious views detection and warning
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+
3
+ sap.ui.define(["sap/base/Log", "sap/ui/model/odata/v2/ODataModel"], function (log, ODataModel) {
4
+ "use strict";
5
+
6
+ /**
7
+ * Returns the base URL for API requests.
8
+ *
9
+ * @returns Base URL string
10
+ */
11
+ function getBaseUrl() {
12
+ return document.getElementById('root')?.dataset.openUxPreviewBaseUrl ?? '';
13
+ }
14
+
15
+ /**
16
+ * Creates a UI5 ODataModel with annotation URLs and returns its metamodel
17
+ * with entity containers, schema, and model information.
18
+ *
19
+ * @param serviceInfo Service URL and annotation details from the server
20
+ * @returns Promise resolving to a metamodel object
21
+ */
22
+ async function buildMetaModel(serviceInfo) {
23
+ const annotationUris = serviceInfo.annotations.map(a => a.Uri);
24
+ const model = new ODataModel(serviceInfo.serviceUrl, {
25
+ annotationURI: annotationUris,
26
+ loadAnnotationsJoined: true,
27
+ skipMetadataAnnotationParsing: false,
28
+ json: true
29
+ });
30
+ const metaModel = model.getMetaModel();
31
+ await metaModel.loaded();
32
+ return {
33
+ oEntityContainers: metaModel.getODataEntityContainer(),
34
+ oSchema: metaModel.getObject('/dataServices/schema'),
35
+ modelInformation: serviceInfo.modelInformation
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Writes i18n entries to the adaptation project's i18n properties file.
41
+ * Fire-and-forget: the OVP runtime does not await the result.
42
+ *
43
+ * @param _path - i18n path (ignored; server resolves from project context)
44
+ * @param properties - Array of i18n key-value entries to write
45
+ */
46
+ function writeToI18n(_path, properties) {
47
+ const entries = properties.map(p => ({
48
+ key: p.key,
49
+ value: p.value,
50
+ annotation: p.textType
51
+ }));
52
+ fetch(`${getBaseUrl()}/editor/i18n`, {
53
+ method: 'POST',
54
+ headers: {
55
+ 'content-type': 'application/json'
56
+ },
57
+ body: JSON.stringify(entries)
58
+ }).catch(error => {
59
+ log.error('OVP writeToI18n failed', error);
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Fetches available OData V2 services from the ABAP system catalog.
65
+ *
66
+ * @param _path - Application path (ignored; server uses its own provider)
67
+ * @returns Promise resolving to an object with a results array of service metadata
68
+ */
69
+ async function getNewDataSources(_path) {
70
+ const response = await fetch(`${getBaseUrl()}/adp/api/ovp/datasources`);
71
+ if (!response.ok) {
72
+ throw new Error(`Failed to fetch data sources: ${response.status}`);
73
+ }
74
+ return response.json();
75
+ }
76
+
77
+ /**
78
+ * Fetches the OData metadata model for a selected data source service.
79
+ * Creates a real UI5 ODataModel with annotations to build a full metamodel
80
+ * compatible with the OVP dialog.
81
+ *
82
+ * @param selectedDataSources - Array of selected data source objects
83
+ * @param _path - Application path (ignored; server uses its own provider)
84
+ * @returns Promise resolving to a metamodel object
85
+ */
86
+ async function getMetaModelForNewDataSource(selectedDataSources, _path) {
87
+ const response = await fetch(`${getBaseUrl()}/adp/api/ovp/metamodel`, {
88
+ method: 'POST',
89
+ headers: {
90
+ 'content-type': 'application/json'
91
+ },
92
+ body: JSON.stringify({
93
+ dataSource: selectedDataSources[0]
94
+ })
95
+ });
96
+ if (!response.ok) {
97
+ throw new Error(`Failed to fetch metamodel: ${response.status}`);
98
+ }
99
+ const serviceInfo = await response.json();
100
+ if (!serviceInfo) {
101
+ return undefined;
102
+ }
103
+ return buildMetaModel(serviceInfo);
104
+ }
105
+
106
+ /**
107
+ * Initializes OVP bridge functions required by the sap.ovp library for the
108
+ * "Add New Card" flow in the adaptation editor.
109
+ *
110
+ * These bridge functions are registered as globals on the window object
111
+ * and connect the OVP runtime (in the preview iframe) to the server-side
112
+ * adaptation tooling middleware via fetch requests.
113
+ */
114
+ function initOvpWindowFunctions() {
115
+ // OVP runtime expects these as window globals
116
+ // eslint-disable-next-line @sap-ux/fiori-tools/sap-no-global-define
117
+ window.writeToI18n = writeToI18n;
118
+ // eslint-disable-next-line @sap-ux/fiori-tools/sap-no-global-define
119
+ window.getNewDataSources = getNewDataSources;
120
+ // eslint-disable-next-line @sap-ux/fiori-tools/sap-no-global-define
121
+ window.getMetaModelForNewDataSource = getMetaModelForNewDataSource;
122
+ log.info('OVP bridge functions initialized (writeToI18n, getNewDataSources, getMetaModelForNewDataSource)');
123
+ }
124
+ var __exports = {
125
+ __esModule: true
126
+ };
127
+ __exports.initOvpWindowFunctions = initOvpWindowFunctions;
128
+ return __exports;
129
+ });
130
+ //# sourceMappingURL=ovp-window-functions.js.map
@@ -0,0 +1,171 @@
1
+ import log from 'sap/base/Log';
2
+ import ODataModel from 'sap/ui/model/odata/v2/ODataModel';
3
+
4
+ interface I18nProperty {
5
+ key: string;
6
+ value: string;
7
+ textType?: string;
8
+ }
9
+
10
+ interface DataSourceInfo {
11
+ ID: string;
12
+ Title: string;
13
+ Description?: string;
14
+ TechnicalServiceName?: string;
15
+ TechnicalServiceVersion?: number;
16
+ ServiceUrl?: string;
17
+ MetadataUrl?: string;
18
+ }
19
+
20
+ interface DataSourcesResponse {
21
+ results: DataSourceInfo[];
22
+ }
23
+
24
+ interface ServiceInfoResponse {
25
+ serviceUrl: string;
26
+ annotations: Array<{ TechnicalName: string; Uri: string }>;
27
+ modelInformation: OvpModelInformation;
28
+ }
29
+
30
+ interface OvpModelInformation {
31
+ serviceURI: string;
32
+ serviceAnnotation: string;
33
+ serviceAnnotationURI: string;
34
+ }
35
+
36
+ interface OvpMetaModelResult {
37
+ oEntityContainers: Record<string, unknown>;
38
+ oSchema: Record<string, unknown>[];
39
+ modelInformation: OvpModelInformation;
40
+ }
41
+
42
+ declare global {
43
+ interface Window {
44
+ writeToI18n: (path: string, properties: I18nProperty[]) => void;
45
+ getNewDataSources: (path: string) => Promise<DataSourcesResponse>;
46
+ getMetaModelForNewDataSource: (
47
+ selectedDataSources: DataSourceInfo[],
48
+ path: string
49
+ ) => Promise<OvpMetaModelResult | undefined>;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Returns the base URL for API requests.
55
+ *
56
+ * @returns Base URL string
57
+ */
58
+ function getBaseUrl(): string {
59
+ return document.getElementById('root')?.dataset.openUxPreviewBaseUrl ?? '';
60
+ }
61
+
62
+ /**
63
+ * Creates a UI5 ODataModel with annotation URLs and returns its metamodel
64
+ * with entity containers, schema, and model information.
65
+ *
66
+ * @param serviceInfo Service URL and annotation details from the server
67
+ * @returns Promise resolving to a metamodel object
68
+ */
69
+ async function buildMetaModel(serviceInfo: ServiceInfoResponse): Promise<OvpMetaModelResult> {
70
+ const annotationUris = serviceInfo.annotations.map((a) => a.Uri);
71
+
72
+ const model = new ODataModel(serviceInfo.serviceUrl, {
73
+ annotationURI: annotationUris,
74
+ loadAnnotationsJoined: true,
75
+ skipMetadataAnnotationParsing: false,
76
+ json: true
77
+ });
78
+
79
+ const metaModel = model.getMetaModel();
80
+ await metaModel.loaded();
81
+
82
+ return {
83
+ oEntityContainers: metaModel.getODataEntityContainer() as Record<string, unknown>,
84
+ oSchema: metaModel.getObject('/dataServices/schema') as Record<string, unknown>[],
85
+ modelInformation: serviceInfo.modelInformation
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Writes i18n entries to the adaptation project's i18n properties file.
91
+ * Fire-and-forget: the OVP runtime does not await the result.
92
+ *
93
+ * @param _path - i18n path (ignored; server resolves from project context)
94
+ * @param properties - Array of i18n key-value entries to write
95
+ */
96
+ function writeToI18n(_path: string, properties: I18nProperty[]): void {
97
+ const entries = properties.map((p) => ({
98
+ key: p.key,
99
+ value: p.value,
100
+ annotation: p.textType
101
+ }));
102
+ fetch(`${getBaseUrl()}/editor/i18n`, {
103
+ method: 'POST',
104
+ headers: { 'content-type': 'application/json' },
105
+ body: JSON.stringify(entries)
106
+ }).catch((error) => {
107
+ log.error('OVP writeToI18n failed', error);
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Fetches available OData V2 services from the ABAP system catalog.
113
+ *
114
+ * @param _path - Application path (ignored; server uses its own provider)
115
+ * @returns Promise resolving to an object with a results array of service metadata
116
+ */
117
+ async function getNewDataSources(_path: string): Promise<DataSourcesResponse> {
118
+ const response = await fetch(`${getBaseUrl()}/adp/api/ovp/datasources`);
119
+ if (!response.ok) {
120
+ throw new Error(`Failed to fetch data sources: ${response.status}`);
121
+ }
122
+ return response.json();
123
+ }
124
+
125
+ /**
126
+ * Fetches the OData metadata model for a selected data source service.
127
+ * Creates a real UI5 ODataModel with annotations to build a full metamodel
128
+ * compatible with the OVP dialog.
129
+ *
130
+ * @param selectedDataSources - Array of selected data source objects
131
+ * @param _path - Application path (ignored; server uses its own provider)
132
+ * @returns Promise resolving to a metamodel object
133
+ */
134
+ async function getMetaModelForNewDataSource(
135
+ selectedDataSources: DataSourceInfo[],
136
+ _path: string
137
+ ): Promise<OvpMetaModelResult | undefined> {
138
+ const response = await fetch(`${getBaseUrl()}/adp/api/ovp/metamodel`, {
139
+ method: 'POST',
140
+ headers: { 'content-type': 'application/json' },
141
+ body: JSON.stringify({ dataSource: selectedDataSources[0] })
142
+ });
143
+ if (!response.ok) {
144
+ throw new Error(`Failed to fetch metamodel: ${response.status}`);
145
+ }
146
+ const serviceInfo = (await response.json()) as ServiceInfoResponse | null;
147
+ if (!serviceInfo) {
148
+ return undefined;
149
+ }
150
+ return buildMetaModel(serviceInfo);
151
+ }
152
+
153
+ /**
154
+ * Initializes OVP bridge functions required by the sap.ovp library for the
155
+ * "Add New Card" flow in the adaptation editor.
156
+ *
157
+ * These bridge functions are registered as globals on the window object
158
+ * and connect the OVP runtime (in the preview iframe) to the server-side
159
+ * adaptation tooling middleware via fetch requests.
160
+ */
161
+ export function initOvpWindowFunctions(): void {
162
+ // OVP runtime expects these as window globals
163
+ // eslint-disable-next-line @sap-ux/fiori-tools/sap-no-global-define
164
+ window.writeToI18n = writeToI18n;
165
+ // eslint-disable-next-line @sap-ux/fiori-tools/sap-no-global-define
166
+ window.getNewDataSources = getNewDataSources;
167
+ // eslint-disable-next-line @sap-ux/fiori-tools/sap-no-global-define
168
+ window.getMetaModelForNewDataSource = getMetaModelForNewDataSource;
169
+
170
+ log.info('OVP bridge functions initialized (writeToI18n, getNewDataSources, getMetaModelForNewDataSource)');
171
+ }
@@ -112,9 +112,19 @@ sap.ui.define(["sap/base/Log", "sap/ui/model/odata/v2/ODataModel", "sap/ui/model
112
112
  () => oModel.destroy());
113
113
  }
114
114
  getServices() {
115
- const manifest = this.rta.getRootControlInstance().getManifest();
115
+ const rootControl = this.rta.getRootControlInstance();
116
+ const manifest = rootControl.getManifest();
116
117
  const dataSources = manifest?.['sap.app']?.dataSources;
117
- return Object.values(dataSources ?? {}).filter(this.isOdataService).map(this.toOdataServiceInfo);
118
+ const odataServices = Object.values(dataSources ?? {}).filter(this.isOdataService);
119
+ const isCloudFoundry = !!this.rta.getFlexSettings().isCloudFoundry;
120
+ if (isCloudFoundry) {
121
+ const manifestObject = rootControl.getManifestObject();
122
+ return odataServices.map(src => ({
123
+ serviceUrl: manifestObject.resolveUri(src.uri),
124
+ oDataVersion: src.settings?.odataVersion ?? ODataHealthChecker.DEFAULT_ODATA_VERSION
125
+ }));
126
+ }
127
+ return odataServices.map(this.toOdataServiceInfo);
118
128
  }
119
129
  }
120
130
  var __exports = {
@@ -141,10 +141,20 @@ export class ODataHealthChecker {
141
141
  }
142
142
 
143
143
  private getServices(): ODataServiceInfo[] {
144
- const manifest: Manifest = this.rta.getRootControlInstance().getManifest() as unknown as Manifest;
144
+ const rootControl = this.rta.getRootControlInstance();
145
+ const manifest: Manifest = rootControl.getManifest() as unknown as Manifest;
145
146
  const dataSources = manifest?.['sap.app']?.dataSources;
146
- return Object.values(dataSources ?? {})
147
- .filter(this.isOdataService)
148
- .map(this.toOdataServiceInfo);
147
+ const odataServices = Object.values(dataSources ?? {}).filter(this.isOdataService);
148
+
149
+ const isCloudFoundry = !!this.rta.getFlexSettings().isCloudFoundry;
150
+ if (isCloudFoundry) {
151
+ const manifestObject = rootControl.getManifestObject();
152
+ return odataServices.map((src) => ({
153
+ serviceUrl: manifestObject.resolveUri(src.uri),
154
+ oDataVersion: (src.settings?.odataVersion ?? ODataHealthChecker.DEFAULT_ODATA_VERSION)
155
+ }));
156
+ }
157
+
158
+ return odataServices.map(this.toOdataServiceInfo);
149
159
  }
150
160
  }
@@ -88,6 +88,7 @@ sap.ui.define(["sap/base/util/merge", "sap/ui/fl/write/api/connectors/ObjectStor
88
88
  if (settings?.scenario === 'ADAPTATION_PROJECT') {
89
89
  features.isVariantAdaptationEnabled = true;
90
90
  }
91
+ features.isAnnotationChangeEnabled = false;
91
92
  return features;
92
93
  }
93
94
  });
@@ -92,6 +92,8 @@ const connector = merge({}, ObjectStorageConnector, {
92
92
  features.isVariantAdaptationEnabled = true;
93
93
  }
94
94
 
95
+ features.isAnnotationChangeEnabled = false;
96
+
95
97
  return features;
96
98
  }
97
99
  }) as typeof ObjectStorageConnector;