@sap-ux/odata-service-writer 0.27.0 → 0.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ import type { Editor } from 'mem-fs-editor';
7
7
  * @param {string} basePath - the root path of an existing UI5 application
8
8
  * @param {OdataService} service - the OData service instance
9
9
  * @param {Editor} fs - the memfs editor instance
10
+ * @param {boolean} update - whether the service update is running (if true, skips unique service name generation and makes sure that '' model is updated for the mainService)
10
11
  */
11
- export declare function enhanceData(basePath: string, service: OdataService, fs: Editor): Promise<void>;
12
+ export declare function enhanceData(basePath: string, service: OdataService, fs: Editor, update?: boolean): Promise<void>;
12
13
  //# sourceMappingURL=defaults.d.ts.map
@@ -16,15 +16,37 @@ const ui5_config_1 = require("@sap-ux/ui5-config");
16
16
  function setDefaultServicePath(service) {
17
17
  service.path = service.path?.endsWith('/') ? service.path : (service.path ?? '') + '/';
18
18
  }
19
+ /**
20
+ * Generates a unique name for the service.
21
+ *
22
+ * @param {OdataService} dataSources - dataSources from manifest.json.
23
+ * @param {string} serviceName - The service name whose name needs to be modified.
24
+ * @returns Unique service name.
25
+ */
26
+ function generateUniqueServiceName(dataSources, serviceName) {
27
+ let tmpSrvName = serviceName;
28
+ let uniqueNameCount = 1;
29
+ let doesExist = true; // Ensure data source name is unique
30
+ do {
31
+ if (dataSources?.[tmpSrvName]) {
32
+ tmpSrvName = `${serviceName}${uniqueNameCount++}`; // Try with a new name
33
+ }
34
+ else {
35
+ doesExist = false;
36
+ }
37
+ } while (doesExist);
38
+ return tmpSrvName;
39
+ }
19
40
  /**
20
41
  * Sets the default name for a given service.
21
42
  * Default serivce name is used only for first service.
22
43
  *
23
44
  * @param {string} basePath - the root path of an existing UI5 application
24
45
  * @param {OdataService} service - The service object whose name needs to be set or modified.
25
- * @param fs - the memfs editor instance
46
+ * @param {Editor} fs - the memfs editor instance
47
+ * @param {boolean} update - whether the service update is running
26
48
  */
27
- async function setDefaultServiceName(basePath, service, fs) {
49
+ async function setDefaultServiceName(basePath, service, fs, update) {
28
50
  const manifestPath = (0, path_1.join)(await (0, project_access_1.getWebappPath)(basePath, fs), project_access_1.FileName.Manifest);
29
51
  const manifest = fs.readJSON(manifestPath);
30
52
  // Check if manifest has already any dataSources defined, DEFAULT_DATASOURCE_NAME should be used for the first service
@@ -35,6 +57,9 @@ async function setDefaultServiceName(basePath, service, fs) {
35
57
  if (oDataSources.length === 0) {
36
58
  service.name = constants_1.DEFAULT_DATASOURCE_NAME;
37
59
  }
60
+ else if (service.name && !update) {
61
+ service.name = generateUniqueServiceName(dataSources, service.name);
62
+ }
38
63
  }
39
64
  else {
40
65
  // No existing dataSources - no existing services, use default name
@@ -48,9 +73,10 @@ async function setDefaultServiceName(basePath, service, fs) {
48
73
  *
49
74
  * @param {string} basePath - the root path of an existing UI5 application
50
75
  * @param {OdataService} service - The service object whose model needs to be set or modified
51
- * @param fs - the memfs editor instance
76
+ * @param {Editor} fs - the memfs editor instance
77
+ * @param {boolean} update - whether the service update is running (if true, makes sure that '' model is updated for the mainService)
52
78
  */
53
- async function setDefaultServiceModel(basePath, service, fs) {
79
+ async function setDefaultServiceModel(basePath, service, fs, update) {
54
80
  const manifestPath = (0, path_1.join)(await (0, project_access_1.getWebappPath)(basePath, fs), 'manifest.json');
55
81
  const manifest = fs.readJSON(manifestPath);
56
82
  if (!service.model) {
@@ -59,12 +85,15 @@ async function setDefaultServiceModel(basePath, service, fs) {
59
85
  if (models) {
60
86
  // Filter dataSource models by dataSource property
61
87
  const servicesModels = Object.values(models).filter((model) => model.dataSource);
62
- service.model =
63
- servicesModels.length === 0 ||
64
- (servicesModels.find((serviceModel) => serviceModel.dataSource === constants_1.DEFAULT_DATASOURCE_NAME) &&
65
- service.name === constants_1.DEFAULT_DATASOURCE_NAME) // model for mainService is ""
66
- ? ''
67
- : service.name;
88
+ if (servicesModels.length === 0 ||
89
+ (update &&
90
+ servicesModels.find((serviceModel) => serviceModel.dataSource === constants_1.DEFAULT_DATASOURCE_NAME) &&
91
+ service.name === constants_1.DEFAULT_DATASOURCE_NAME)) {
92
+ service.model = '';
93
+ }
94
+ else if (service.name) {
95
+ service.model = service.name;
96
+ }
68
97
  }
69
98
  else {
70
99
  // No models defined, that means first one is being added, set model to ''
@@ -147,11 +176,14 @@ async function setDefaultPreviewSettings(basePath, service, fs) {
147
176
  * @param {string} basePath - the root path of an existing UI5 application
148
177
  * @param {OdataService} service - the OData service instance
149
178
  * @param {Editor} fs - the memfs editor instance
179
+ * @param {boolean} update - whether the service update is running (if true, skips unique service name generation and makes sure that '' model is updated for the mainService)
150
180
  */
151
- async function enhanceData(basePath, service, fs) {
152
- setDefaultServicePath(service);
153
- await setDefaultServiceName(basePath, service, fs);
154
- await setDefaultServiceModel(basePath, service, fs);
181
+ async function enhanceData(basePath, service, fs, update = false) {
182
+ if (!update) {
183
+ setDefaultServicePath(service);
184
+ }
185
+ await setDefaultServiceName(basePath, service, fs, update);
186
+ await setDefaultServiceModel(basePath, service, fs, update);
155
187
  // set service type to EDMX if not defined
156
188
  service.type = service.type ?? types_1.ServiceType.EDMX;
157
189
  /**
@@ -403,17 +403,26 @@ async function updateManifest(basePath, service, fs, forceServiceUpdate = false)
403
403
  const manifest = fs.readJSON(manifestPath);
404
404
  const appProp = 'sap.app';
405
405
  const appid = manifest?.[appProp]?.id;
406
+ const dataSources = manifest[appProp]?.dataSources ?? {};
406
407
  // Throw if required property is not found manifest.json
407
408
  if (!appid) {
408
409
  throw new Error((0, i18n_1.t)('error.requiredProjectPropertyNotFound', { property: `'${appProp}'.id`, path: manifestPath }));
409
410
  }
410
411
  // Throw if only update is required and service is not found in manifest.json
411
- if (forceServiceUpdate && service.name && !manifest[appProp]?.dataSources?.[service.name]) {
412
+ if (forceServiceUpdate && service.name && !dataSources?.[service.name]) {
412
413
  throw new Error((0, i18n_1.t)('error.requiredProjectPropertyNotFound', {
413
414
  property: `'${appProp}.dataSources.${service.name}'`,
414
415
  path: manifestPath
415
416
  }));
416
417
  }
418
+ // Throw if service is being added, but service with the same URI already exists
419
+ if (!forceServiceUpdate &&
420
+ service.path &&
421
+ Object.values(dataSources).find((dataSource) => dataSource.uri === service.path)) {
422
+ throw new Error((0, i18n_1.t)('error.requiredServiceAlreadyExists', {
423
+ uri: service.path
424
+ }));
425
+ }
417
426
  // Check and update existing services in a way that is supported by multiple services
418
427
  const convertedManifest = await addMultipleServiceSupportToManifest(webappPath, manifest, fs);
419
428
  // Update manifest.json services
package/dist/index.d.ts CHANGED
@@ -17,10 +17,11 @@ declare function generate(basePath: string, service: OdataService, fs?: Editor):
17
17
  * @param {string} basePath - the root path of an existing UI5 application
18
18
  * @param {OdataService} service - the OData service instance
19
19
  * @param {Editor} [fs] - the memfs editor instance
20
+ * @param {boolean} updateMiddlewares - whether the YAML files for the service (mock-server and fiori-tools-proxy middlewares) should be updated
20
21
  * @throws {Error} - if required UI5 project files are not found
21
22
  * @returns {Promise<Editor>} the updated memfs editor instance
22
23
  */
23
- declare function update(basePath: string, service: OdataService, fs?: Editor): Promise<Editor>;
24
+ declare function update(basePath: string, service: OdataService, fs?: Editor, updateMiddlewares?: boolean): Promise<Editor>;
24
25
  /**
25
26
  * Removes service related data from project files for an existing UI5 project specified by the base path.
26
27
  * How the method works:
package/dist/index.js CHANGED
@@ -97,22 +97,23 @@ async function generate(basePath, service, fs) {
97
97
  * @param {string} basePath - the root path of an existing UI5 application
98
98
  * @param {OdataService} service - the OData service instance
99
99
  * @param {Editor} [fs] - the memfs editor instance
100
+ * @param {boolean} updateMiddlewares - whether the YAML files for the service (mock-server and fiori-tools-proxy middlewares) should be updated
100
101
  * @throws {Error} - if required UI5 project files are not found
101
102
  * @returns {Promise<Editor>} the updated memfs editor instance
102
103
  */
103
- async function update(basePath, service, fs) {
104
+ async function update(basePath, service, fs, updateMiddlewares = true) {
104
105
  if (!fs) {
105
106
  fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
106
107
  }
107
108
  const paths = await findProjectFiles(basePath, fs);
108
109
  const webappPath = await (0, project_access_1.getWebappPath)(basePath, fs);
109
110
  ensureExists(webappPath, ['manifest.json'], fs);
110
- await (0, data_1.enhanceData)(basePath, service, fs);
111
+ await (0, data_1.enhanceData)(basePath, service, fs, true);
111
112
  // Set isServiceTypeEdmx true if service is EDMX
112
113
  const isServiceTypeEdmx = service.type === types_1.ServiceType.EDMX;
113
114
  await (0, manifest_1.updateManifest)(basePath, service, fs, true);
114
115
  // Dont extend/update backend and mockserver middlewares if service type is CDS
115
- if (isServiceTypeEdmx) {
116
+ if (isServiceTypeEdmx && updateMiddlewares) {
116
117
  await (0, update_1.updateServicesData)(basePath, paths, service, fs);
117
118
  }
118
119
  return fs;
@@ -2,6 +2,7 @@
2
2
  "error": {
3
3
  "requiredProjectFileNotFound": "Invalid project structure. Cannot find required file {{-path}}",
4
4
  "requiredProjectPropertyNotFound": "Required project property: {{-property}} was not found in file: {{-path}}",
5
+ "requiredServiceAlreadyExists": "A service with the {{-uri}} URI already exists. Choose another service.",
5
6
  "unparseableXML": "Unparseable XML was specified: {{-error}}"
6
7
  }
7
8
  }
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ManifestNamespace } from '@sap-ux/project-access';
1
2
  import type { FioriToolsProxyConfigBackend as ProxyBackend } from '@sap-ux/ui5-config';
2
3
  export declare enum OdataVersion {
3
4
  v2 = "2",
@@ -82,4 +83,7 @@ export interface ProjectPaths {
82
83
  ui5LocalYaml?: string;
83
84
  ui5MockYaml?: string;
84
85
  }
86
+ export type DataSources = {
87
+ [k: string]: ManifestNamespace.DataSource;
88
+ };
85
89
  //# sourceMappingURL=types.d.ts.map
package/dist/update.d.ts CHANGED
@@ -15,7 +15,6 @@ export declare function addServicesData(basePath: string, paths: ProjectPaths, t
15
15
  /**
16
16
  * Updates services data in ui5-*.yaml files.
17
17
  * Mockserver configuration for services and annotations are updated using dataSources from manifest.json.
18
- * At the end, older XML files for service annotations are removed and new annotations are generated.
19
18
  *
20
19
  * @param {string} basePath - the root path of an existing UI5 application
21
20
  * @param {ProjectPaths} paths - paths to the project files (package.json, ui5.yaml, ui5-local.yaml and ui5-mock.yaml)
package/dist/update.js CHANGED
@@ -27,28 +27,36 @@ async function generateMockserverMiddlewareBasedOnUi5MockYaml(fs, ui5YamlPath, u
27
27
  }
28
28
  }
29
29
  /**
30
- * Extends backend middleware for UI5Config with service data.
30
+ * Extends backend middleware for UI5Config with the service data.
31
31
  *
32
32
  * @param {Editor} fs - the memfs editor instance
33
33
  * @param {OdataService} service - the OData service instance data
34
34
  * @param {UI5Config} ui5Config - UI5 configuration
35
35
  * @param {string} ui5ConfigPath - path to the YAML config file
36
+ * @param {boolean} update - whether the service update is running
36
37
  * @throws {Error} - if required UI5 project files are not found
37
38
  */
38
- function extendBackendMiddleware(fs, service, ui5Config, ui5ConfigPath) {
39
- try {
40
- ui5Config.addBackendToFioriToolsProxydMiddleware(service.previewSettings, service.ignoreCertError);
39
+ function extendBackendMiddleware(fs, service, ui5Config, ui5ConfigPath, update = false) {
40
+ if (update) {
41
+ ui5Config.updateBackendToFioriToolsProxydMiddleware(service.previewSettings);
41
42
  }
42
- catch (error) {
43
- if ((error instanceof ui5_config_1.YAMLError && error.code === ui5_config_1.yamlErrorCode.nodeNotFound) ||
44
- error.message === 'Could not find fiori-tools-proxy') {
45
- ui5Config.addFioriToolsProxydMiddleware({
46
- backend: [service.previewSettings],
47
- ignoreCertError: service.ignoreCertError
48
- });
43
+ else {
44
+ // Try to add backend
45
+ try {
46
+ ui5Config.addBackendToFioriToolsProxydMiddleware(service.previewSettings, service.ignoreCertError);
49
47
  }
50
- else {
51
- throw error;
48
+ catch (error) {
49
+ if ((error instanceof ui5_config_1.YAMLError && error.code === ui5_config_1.yamlErrorCode.nodeNotFound) ||
50
+ error.message === 'Could not find fiori-tools-proxy') {
51
+ // Middleware is missing, add it along with the service backend
52
+ ui5Config.addFioriToolsProxydMiddleware({
53
+ backend: [service.previewSettings],
54
+ ignoreCertError: service.ignoreCertError
55
+ });
56
+ }
57
+ else {
58
+ throw error;
59
+ }
52
60
  }
53
61
  }
54
62
  fs.write(ui5ConfigPath, ui5Config.toString());
@@ -108,7 +116,6 @@ async function addServicesData(basePath, paths, templateRoot, service, fs) {
108
116
  /**
109
117
  * Updates services data in ui5-*.yaml files.
110
118
  * Mockserver configuration for services and annotations are updated using dataSources from manifest.json.
111
- * At the end, older XML files for service annotations are removed and new annotations are generated.
112
119
  *
113
120
  * @param {string} basePath - the root path of an existing UI5 application
114
121
  * @param {ProjectPaths} paths - paths to the project files (package.json, ui5.yaml, ui5-local.yaml and ui5-mock.yaml)
@@ -118,37 +125,45 @@ async function addServicesData(basePath, paths, templateRoot, service, fs) {
118
125
  async function updateServicesData(basePath, paths, service, fs) {
119
126
  let ui5Config;
120
127
  let ui5LocalConfig;
128
+ let ui5MockConfig;
121
129
  if (paths.ui5Yaml) {
122
130
  ui5Config = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5Yaml));
131
+ // Update ui5.yaml with backend middleware
132
+ extendBackendMiddleware(fs, service, ui5Config, paths.ui5Yaml, true);
123
133
  }
134
+ // Update ui5-local.yaml with backend middleware
124
135
  if (paths.ui5LocalYaml) {
125
136
  ui5LocalConfig = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5LocalYaml));
137
+ extendBackendMiddleware(fs, service, ui5LocalConfig, paths.ui5LocalYaml, true);
126
138
  }
127
139
  // For update, updatable files should already exist
128
140
  if (service.metadata) {
129
141
  const webappPath = await (0, project_access_1.getWebappPath)(basePath, fs);
130
142
  // Generate mockserver only when ui5-mock.yaml already exists
131
- if (paths.ui5MockYaml) {
132
- if (paths.ui5Yaml && ui5Config) {
133
- const config = {
134
- webappPath: webappPath,
135
- // Since ui5-mock.yaml already exists, set 'skip' to skip package.json file updates
136
- packageJsonConfig: {
137
- skip: true
138
- },
139
- // Set 'overwrite' to true to overwrite services data in YAML files
140
- ui5MockYamlConfig: {
141
- overwrite: true
142
- }
143
- };
144
- // Regenerate mockserver middleware for ui5-mock.yaml by overwriting
145
- await (0, mockserver_config_writer_1.generateMockserverConfig)(basePath, config, fs);
146
- // Update ui5-local.yaml with mockserver middleware from updated ui5-mock.yaml
147
- await generateMockserverMiddlewareBasedOnUi5MockYaml(fs, paths.ui5Yaml, paths.ui5LocalYaml, ui5LocalConfig);
148
- if (paths.ui5LocalYaml && ui5LocalConfig) {
149
- // write ui5 local yaml if service type is not CDS
150
- fs.write(paths.ui5LocalYaml, ui5LocalConfig.toString());
143
+ if (paths.ui5MockYaml && paths.ui5Yaml && ui5Config) {
144
+ const config = {
145
+ webappPath: webappPath,
146
+ // Since ui5-mock.yaml already exists, set 'skip' to skip package.json file updates
147
+ packageJsonConfig: {
148
+ skip: true
149
+ },
150
+ // Set 'overwrite' to true to overwrite services data in YAML files
151
+ ui5MockYamlConfig: {
152
+ overwrite: true
151
153
  }
154
+ };
155
+ // Regenerate mockserver middleware for ui5-mock.yaml by overwriting
156
+ await (0, mockserver_config_writer_1.generateMockserverConfig)(basePath, config, fs);
157
+ // Update ui5-local.yaml with mockserver middleware from updated ui5-mock.yaml
158
+ await generateMockserverMiddlewareBasedOnUi5MockYaml(fs, paths.ui5Yaml, paths.ui5LocalYaml, ui5LocalConfig);
159
+ // Update ui5-mock.yaml with backend middleware
160
+ if (paths.ui5MockYaml) {
161
+ ui5MockConfig = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5MockYaml));
162
+ extendBackendMiddleware(fs, service, ui5MockConfig, paths.ui5MockYaml, true);
163
+ }
164
+ if (paths.ui5LocalYaml && ui5LocalConfig) {
165
+ // write ui5 local yaml if service type is not CDS
166
+ fs.write(paths.ui5LocalYaml, ui5LocalConfig.toString());
152
167
  }
153
168
  }
154
169
  // Write metadata.xml file
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%3Aodata-service-writer"
11
11
  },
12
- "version": "0.27.0",
12
+ "version": "0.27.2",
13
13
  "license": "Apache-2.0",
14
14
  "main": "dist/index.js",
15
15
  "files": [
@@ -27,9 +27,9 @@
27
27
  "mem-fs-editor": "9.4.0",
28
28
  "prettify-xml": "1.2.0",
29
29
  "semver": "7.5.4",
30
- "@sap-ux/mockserver-config-writer": "0.9.0",
31
- "@sap-ux/project-access": "1.30.0",
32
- "@sap-ux/ui5-config": "0.28.0"
30
+ "@sap-ux/mockserver-config-writer": "0.9.1",
31
+ "@sap-ux/project-access": "1.30.1",
32
+ "@sap-ux/ui5-config": "0.28.1"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/ejs": "3.1.2",