@sap-ux/odata-service-writer 0.23.2 → 0.24.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.
@@ -4,25 +4,19 @@ exports.getAnnotationNamespaces = getAnnotationNamespaces;
4
4
  const fast_xml_parser_1 = require("fast-xml-parser");
5
5
  const i18n_1 = require("../i18n");
6
6
  /**
7
- * Returns the namespaces parsed from the specified metadata and annotations.
7
+ * Returns the namespaces parsed from the specified metadata and single annotation.
8
8
  *
9
- * @param {Partial<OdataService>} service - an odata service where at least metadata and annotations properties are defined
10
- * @param {string} service.metadata - OData service metadata xml
11
- * @param {string} service.annotations - OData service annotations xml
9
+ * @param {EdmxAnnotationsInfo} edmxAnnotation - OData service annotations xml
10
+ * @param {NamespaceAlias[]} schemaNamespaces - namespaces array from metadata
12
11
  * @returns A reference to the namspaces array
13
12
  */
14
- function getAnnotationNamespaces({ metadata, annotations }) {
15
- // Enhance service with annotations namespaces
16
- const schemaNamespaces = metadata ? getNamespaces(metadata) : [];
17
- const edmxAnnotations = annotations;
18
- if (edmxAnnotations?.xml) {
13
+ function getAnnotationNamespacesForSingleAnnotation(edmxAnnotation, schemaNamespaces) {
14
+ if (edmxAnnotation?.xml) {
19
15
  // Parse once
20
- const annotationsJson = xmlToJson(edmxAnnotations.xml);
16
+ const annotationsJson = xmlToJson(edmxAnnotation.xml);
21
17
  return schemaNamespaces.map((schema) => {
22
18
  // Check if alias exists in backend annotation file, if so use it
23
- const annotationAlias = edmxAnnotations.xml && schema.namespace
24
- ? getAliasFromAnnotation(annotationsJson, schema.namespace)
25
- : '';
19
+ const annotationAlias = edmxAnnotation.xml && schema.namespace ? getAliasFromAnnotation(annotationsJson, schema.namespace) : '';
26
20
  if (annotationAlias) {
27
21
  schema.alias = annotationAlias;
28
22
  }
@@ -31,6 +25,29 @@ function getAnnotationNamespaces({ metadata, annotations }) {
31
25
  }
32
26
  return schemaNamespaces;
33
27
  }
28
+ /**
29
+ * Returns the namespaces parsed from the specified metadata and annotations.
30
+ *
31
+ * @param {Partial<OdataService>} service - an odata service where at least metadata and annotations properties are defined
32
+ * @param {string} service.metadata - OData service metadata xml
33
+ * @param {string} service.annotations - OData service annotations xml
34
+ * @returns A reference to the namspaces array
35
+ */
36
+ function getAnnotationNamespaces({ metadata, annotations }) {
37
+ // Enhance service with annotations namespaces
38
+ let schemaNamespaces = metadata ? getNamespaces(metadata) : [];
39
+ if (Array.isArray(annotations)) {
40
+ for (const annotationName in annotations) {
41
+ const edmxAnnotation = annotations[annotationName];
42
+ schemaNamespaces = getAnnotationNamespacesForSingleAnnotation(edmxAnnotation, schemaNamespaces);
43
+ }
44
+ }
45
+ else {
46
+ const edmxAnnotation = annotations;
47
+ schemaNamespaces = getAnnotationNamespacesForSingleAnnotation(edmxAnnotation, schemaNamespaces);
48
+ }
49
+ return schemaNamespaces;
50
+ }
34
51
  /**
35
52
  * Convert specified xml string to JSON.
36
53
  *
@@ -1,9 +1,12 @@
1
1
  import type { OdataService } from '../types';
2
+ import type { Editor } from 'mem-fs-editor';
2
3
  /**
3
4
  * Enhances the provided OData service object with path, name and model information.
4
5
  * Directly modifies the passed object reference.
5
6
  *
6
- * @param {OdataService} service - the OData service object
7
+ * @param {string} basePath - the root path of an existing UI5 application
8
+ * @param {OdataService} service - the OData service instance
9
+ * @param {Editor} fs - the memfs editor instance
7
10
  */
8
- export declare function enhanceData(service: OdataService): void;
11
+ export declare function enhanceData(basePath: string, service: OdataService, fs: Editor): Promise<void>;
9
12
  //# sourceMappingURL=defaults.d.ts.map
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enhanceData = enhanceData;
4
+ const path_1 = require("path");
4
5
  const types_1 = require("../types");
5
6
  const constants_1 = require("./constants");
7
+ const project_access_1 = require("@sap-ux/project-access");
6
8
  /**
7
9
  * Sets the default path for a given service.
8
10
  * If the service path is not defined, it sets the path to '/'.
@@ -15,45 +17,99 @@ function setDefaultServicePath(service) {
15
17
  }
16
18
  /**
17
19
  * Sets the default name for a given service.
18
- * If the service name is not defined, it sets the name to `DEFAULT_DATASOURCE_NAME`.
20
+ * Default serivce name is used only for first service.
19
21
  *
22
+ * @param {string} basePath - the root path of an existing UI5 application
20
23
  * @param {OdataService} service - The service object whose name needs to be set or modified.
24
+ * @param fs - the memfs editor instance
21
25
  */
22
- function setDefaultServiceName(service) {
23
- service.name = service.name ?? constants_1.DEFAULT_DATASOURCE_NAME;
26
+ async function setDefaultServiceName(basePath, service, fs) {
27
+ const manifestPath = (0, path_1.join)(await (0, project_access_1.getWebappPath)(basePath, fs), project_access_1.FileName.Manifest);
28
+ const manifest = fs.readJSON(manifestPath);
29
+ // Check if manifest has already any dataSources defined, DEFAULT_DATASOURCE_NAME should be used for the first service
30
+ const dataSources = manifest?.['sap.app']?.dataSources;
31
+ if (dataSources) {
32
+ // Filter out ODataAnnotation dataSources and keep only OData ones
33
+ const oDataSources = Object.values(dataSources).filter((dataSource) => dataSource.type === 'OData');
34
+ if (oDataSources.length === 0) {
35
+ service.name = constants_1.DEFAULT_DATASOURCE_NAME;
36
+ }
37
+ }
38
+ else {
39
+ // No existing dataSources - no existing services, use default name
40
+ service.name = constants_1.DEFAULT_DATASOURCE_NAME;
41
+ }
24
42
  }
25
43
  /**
26
44
  * Sets the default model for a given service.
27
- * If the service model is not defined, it sets the model to an empty string (Default UI5 model).
45
+ * Default UI5 model is used for first service model.
46
+ * For next services service model or service name is used as model (if model is not defined).
28
47
  *
29
- * @param {OdataService} service - The service object whose model needs to be set or modified.
48
+ * @param {string} basePath - the root path of an existing UI5 application
49
+ * @param {OdataService} service - The service object whose model needs to be set or modified
50
+ * @param fs - the memfs editor instance
30
51
  */
31
- function setDefaultServiceModel(service) {
32
- service.model = service.model ?? ''; // Default UI5 model
52
+ async function setDefaultServiceModel(basePath, service, fs) {
53
+ const manifestPath = (0, path_1.join)(await (0, project_access_1.getWebappPath)(basePath, fs), 'manifest.json');
54
+ const manifest = fs.readJSON(manifestPath);
55
+ // Check if manifest has already any dataSource models defined, empty string '' should be used for the first service
56
+ const models = manifest?.['sap.ui5']?.models;
57
+ if (models) {
58
+ // Filter dataSource models by dataSource property
59
+ const servicesModels = Object.values(models).filter((model) => model.dataSource);
60
+ service.model = servicesModels.length === 0 ? '' : service.model ?? service.name;
61
+ }
62
+ // No models defined, that means first one is being added, set model to ''
63
+ service.model ??= '';
33
64
  }
34
65
  /**
35
- * Sets the default annotations name for a given service.
36
- * If the service annotations name is not defined or empty, it creates a default annotations name
66
+ * Sets default annotation name for a single annotation of a given service.
67
+ * If the service annotation name is not defined or empty, it creates a default annotations name
37
68
  * from the technicalName by replacing all '/' characters with '_' and removing the leading '_'.
69
+ * If the service and annotation names are the same, then '_Annotation' string is added at the end of annotation name.
70
+ *
71
+ * @param {EdmxAnnotationsInfo} annotation - annotation of a given service
72
+ * @param {string} serviceName - name of the service whose annotations are getting modified.
73
+ */
74
+ function setDefaultAnnotationName(annotation, serviceName) {
75
+ if (annotation?.technicalName && !annotation.name) {
76
+ annotation.name = annotation?.technicalName?.replace(/\//g, '_')?.replace(/^_/, '');
77
+ }
78
+ if (annotation.name === serviceName) {
79
+ annotation.name += '_Annotation';
80
+ }
81
+ }
82
+ /**
83
+ * Sets default names for annotations of a given service.
84
+ * Handles single annotation in object or annotations array.
38
85
  *
39
86
  * @param {OdataService} service - The service object whose annotations name needs to be set or modified.
40
87
  */
41
88
  function setDefaultAnnotationsName(service) {
42
- const annotations = service.annotations;
43
- if (annotations?.technicalName && !annotations.name) {
44
- annotations.name = annotations?.technicalName?.replace(/\//g, '_')?.replace(/^_/, '');
89
+ if (Array.isArray(service.annotations)) {
90
+ const annotations = service.annotations;
91
+ for (const annotationName in annotations) {
92
+ const annotation = annotations[annotationName];
93
+ setDefaultAnnotationName(annotation, service.name);
94
+ }
95
+ }
96
+ else if (service.annotations) {
97
+ const annotation = service.annotations;
98
+ setDefaultAnnotationName(annotation, service.name);
45
99
  }
46
100
  }
47
101
  /**
48
102
  * Enhances the provided OData service object with path, name and model information.
49
103
  * Directly modifies the passed object reference.
50
104
  *
51
- * @param {OdataService} service - the OData service object
105
+ * @param {string} basePath - the root path of an existing UI5 application
106
+ * @param {OdataService} service - the OData service instance
107
+ * @param {Editor} fs - the memfs editor instance
52
108
  */
53
- function enhanceData(service) {
109
+ async function enhanceData(basePath, service, fs) {
54
110
  setDefaultServicePath(service);
55
- setDefaultServiceName(service);
56
- setDefaultServiceModel(service);
111
+ await setDefaultServiceName(basePath, service, fs);
112
+ await setDefaultServiceModel(basePath, service, fs);
57
113
  // set service type to EDMX if not defined
58
114
  service.type = service.type ?? types_1.ServiceType.EDMX;
59
115
  /**
@@ -65,10 +121,10 @@ function enhanceData(service) {
65
121
  setDefaultAnnotationsName(service);
66
122
  }
67
123
  // enhance preview settings with service configuration
68
- service.previewSettings = service.previewSettings || {};
124
+ service.previewSettings = service.previewSettings ?? {};
69
125
  service.previewSettings.path =
70
- service.previewSettings.path || `/${service.path?.split('/').filter((s) => s !== '')[0] ?? ''}`;
71
- service.previewSettings.url = service.previewSettings.url || service.url || 'http://localhost';
126
+ service.previewSettings.path ?? `/${service.path?.split('/').filter((s) => s !== '')[0] ?? ''}`;
127
+ service.previewSettings.url = service.previewSettings.url ?? service.url ?? 'http://localhost';
72
128
  if (service.client && !service.previewSettings.client) {
73
129
  service.previewSettings.client = service.client;
74
130
  }
@@ -0,0 +1,30 @@
1
+ import type { Editor } from 'mem-fs-editor';
2
+ import type { CdsAnnotationsInfo, EdmxAnnotationsInfo } from './types';
3
+ /**
4
+ * Removes annotations from CDS files.
5
+ * This function takes cds annotations and an Editor instance,
6
+ * then updates the relevant cds files with the given annotations.
7
+ *
8
+ * @param {CdsAnnotationsInfo} annotations - The cds annotations info.
9
+ * @param {Editor} fs - The memfs editor instance
10
+ * @returns {Promise<void>} A promise that resolves when the cds files have been updated.
11
+ */
12
+ export declare function removeAnnotationsFromCDSFiles(annotations: CdsAnnotationsInfo | CdsAnnotationsInfo[], fs: Editor): Promise<void>;
13
+ /**
14
+ * Removes annotation XML files for EDMX annotations.
15
+ *
16
+ * @param {Editor} fs - The memfs editor instance.
17
+ * @param {string} basePath - The base path of the project.
18
+ * @param {string} serviceName - Name of The OData service.
19
+ * @param {OdataService} edmxAnnotations - The OData service annotations.
20
+ */
21
+ export declare function removeAnnotationXmlFiles(fs: Editor, basePath: string, serviceName: string, edmxAnnotations: EdmxAnnotationsInfo | EdmxAnnotationsInfo[]): void;
22
+ /**
23
+ * Internal function that deletes service from the manifest.json based on the given service name.
24
+ *
25
+ * @param basePath - the root path of an existing UI5 application
26
+ * @param serviceName - name of the OData service instance
27
+ * @param fs - the memfs editor instance
28
+ */
29
+ export declare function deleteServiceFromManifest(basePath: string, serviceName: string, fs: Editor): void;
30
+ //# sourceMappingURL=delete.d.ts.map
package/dist/delete.js ADDED
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.removeAnnotationsFromCDSFiles = removeAnnotationsFromCDSFiles;
4
+ exports.removeAnnotationXmlFiles = removeAnnotationXmlFiles;
5
+ exports.deleteServiceFromManifest = deleteServiceFromManifest;
6
+ const path_1 = require("path");
7
+ const i18n_1 = require("./i18n");
8
+ /**
9
+ * Removes the cds index or service file with the provided annotations.
10
+ * This function takes an Editor instance and cds annotations
11
+ * and deletes either from the index file or the service file with the given annotations.
12
+ *
13
+ * @param {Editor} fs - The memfs editor instance
14
+ * @param {CdsAnnotationsInfo} annotations - The cds annotations info.
15
+ * @returns {Promise<void>} A promise that resolves when the cds files have been updated.
16
+ */
17
+ async function removeCdsIndexOrServiceFile(fs, annotations) {
18
+ const dirPath = (0, path_1.join)(annotations.projectName, 'annotations');
19
+ const annotationPath = (0, path_1.normalize)(dirPath).split(/[\\/]/g).join(path_1.posix.sep);
20
+ const annotationConfig = `\nusing from './${annotationPath}';`;
21
+ // Get index and service file paths
22
+ const indexFilePath = (0, path_1.join)(annotations.projectPath, annotations.appPath ?? '', 'index.cds');
23
+ const serviceFilePath = (0, path_1.join)(annotations.projectPath, annotations.appPath ?? '', 'services.cds');
24
+ // Remove annotation config from index or service file
25
+ if (indexFilePath && fs.exists(indexFilePath)) {
26
+ // Read old annotations content and replace it with empty string
27
+ const initialIndexContent = fs.read(indexFilePath);
28
+ const updatedContent = initialIndexContent.replace(annotationConfig, '');
29
+ fs.write(indexFilePath, updatedContent);
30
+ }
31
+ else if (fs.exists(serviceFilePath)) {
32
+ // Read old annotations content and replace it with empty string
33
+ const initialServiceFileContent = fs.read(serviceFilePath);
34
+ const updatedContent = initialServiceFileContent.replace(annotationConfig, '');
35
+ fs.write(serviceFilePath, updatedContent);
36
+ }
37
+ }
38
+ /**
39
+ * Removes annotations from CDS files.
40
+ * This function takes cds annotations and an Editor instance,
41
+ * then updates the relevant cds files with the given annotations.
42
+ *
43
+ * @param {CdsAnnotationsInfo} annotations - The cds annotations info.
44
+ * @param {Editor} fs - The memfs editor instance
45
+ * @returns {Promise<void>} A promise that resolves when the cds files have been updated.
46
+ */
47
+ async function removeAnnotationsFromCDSFiles(annotations, fs) {
48
+ if (Array.isArray(annotations)) {
49
+ for (const annotationName in annotations) {
50
+ const annotation = annotations[annotationName];
51
+ const annotationCdsPath = (0, path_1.join)(annotation.projectPath, annotation.appPath ?? '', annotation.projectName, 'annotations.cds');
52
+ // Remove from annotations.cds file
53
+ if (fs.exists(annotationCdsPath)) {
54
+ // Read old annotations content and replace it with empty string
55
+ const initialCDSContent = fs.read(annotationCdsPath);
56
+ const updatedContent = initialCDSContent.replace(annotation.cdsFileContents, '');
57
+ fs.write(annotationCdsPath, updatedContent);
58
+ }
59
+ await removeCdsIndexOrServiceFile(fs, annotation);
60
+ }
61
+ }
62
+ else {
63
+ const annotationCdsPath = (0, path_1.join)(annotations.projectPath, annotations.appPath ?? '', annotations.projectName, 'annotations.cds');
64
+ // Write into annotations.cds file
65
+ if (fs.exists(annotationCdsPath)) {
66
+ // Read old annotations content and replace it with empty string
67
+ const initialCDSContent = fs.read(annotationCdsPath);
68
+ const updatedContent = initialCDSContent.replace(annotations.cdsFileContents, '');
69
+ fs.write(annotationCdsPath, updatedContent);
70
+ }
71
+ await removeCdsIndexOrServiceFile(fs, annotations);
72
+ }
73
+ }
74
+ /**
75
+ * Removes annotation XML files for EDMX annotations.
76
+ *
77
+ * @param {Editor} fs - The memfs editor instance.
78
+ * @param {string} basePath - The base path of the project.
79
+ * @param {string} serviceName - Name of The OData service.
80
+ * @param {OdataService} edmxAnnotations - The OData service annotations.
81
+ */
82
+ function removeAnnotationXmlFiles(fs, basePath, serviceName, edmxAnnotations) {
83
+ // Write annotation xml if annotations are provided and service type is EDMX
84
+ if (Array.isArray(edmxAnnotations)) {
85
+ for (const annotationName in edmxAnnotations) {
86
+ const annotation = edmxAnnotations[annotationName];
87
+ const pathToAnnotationFile = (0, path_1.join)(basePath, 'webapp', 'localService', serviceName, `${annotation.technicalName}.xml`);
88
+ if (fs.exists(pathToAnnotationFile)) {
89
+ fs.delete(pathToAnnotationFile);
90
+ }
91
+ }
92
+ }
93
+ else if (edmxAnnotations?.xml) {
94
+ const pathToAnnotationFile = (0, path_1.join)(basePath, 'webapp', 'localService', serviceName, `${edmxAnnotations.technicalName}.xml`);
95
+ if (fs.exists(pathToAnnotationFile)) {
96
+ fs.delete(pathToAnnotationFile);
97
+ }
98
+ }
99
+ }
100
+ /**
101
+ * Internal function that removes files related to dataSource.
102
+ *
103
+ * @param fs - the memfs editor instance
104
+ * @param manifestPath - the root path of an existing UI5 application
105
+ * @param dataSource - name of the OData service instance
106
+ */
107
+ function removeFileForDataSource(fs, manifestPath, dataSource) {
108
+ const serviceSettings = dataSource.settings || {};
109
+ if (serviceSettings?.localUri) {
110
+ const localUriPath = (0, path_1.join)((0, path_1.dirname)(manifestPath), serviceSettings?.localUri);
111
+ if (fs.exists(localUriPath)) {
112
+ // delete the local data source file
113
+ fs.delete(localUriPath);
114
+ }
115
+ }
116
+ }
117
+ /**
118
+ * Internal function that removes annotation files related to service.
119
+ *
120
+ * @param fs - the memfs editor instance
121
+ * @param manifestPath - the root path of an existing UI5 application
122
+ * @param annotations - annotations list
123
+ * @param dataSources - list of dataSources from manifest.json
124
+ */
125
+ function removeAnnotations(fs, manifestPath, annotations, dataSources) {
126
+ for (const datasourceKey of annotations) {
127
+ const annotationDatasource = dataSources?.[datasourceKey];
128
+ if (annotationDatasource?.type === 'ODataAnnotation') {
129
+ if (annotationDatasource.uri === annotationDatasource?.settings?.localUri) {
130
+ // This is localAnnotaton file. Do not delete it.
131
+ }
132
+ else if (annotationDatasource) {
133
+ removeFileForDataSource(fs, manifestPath, annotationDatasource);
134
+ // delete dataSource from manifest
135
+ delete dataSources?.[datasourceKey];
136
+ }
137
+ }
138
+ }
139
+ }
140
+ /**
141
+ * Internal function that deletes service from the manifest.json based on the given service name.
142
+ *
143
+ * @param basePath - the root path of an existing UI5 application
144
+ * @param serviceName - name of the OData service instance
145
+ * @param fs - the memfs editor instance
146
+ */
147
+ function deleteServiceFromManifest(basePath, serviceName, fs) {
148
+ const manifestPath = (0, path_1.join)(basePath, 'webapp', 'manifest.json');
149
+ // Get component app id
150
+ const manifest = fs.readJSON(manifestPath);
151
+ const appProp = 'sap.app';
152
+ const appid = manifest?.[appProp]?.id;
153
+ // Throw if required property is not found manifest.json
154
+ if (!appid) {
155
+ throw new Error((0, i18n_1.t)('error.requiredProjectPropertyNotFound', { property: `'${appProp}'.id`, path: manifestPath }));
156
+ }
157
+ const dataSources = manifest?.[appProp]?.dataSources;
158
+ if (dataSources?.[serviceName]) {
159
+ removeFileForDataSource(fs, manifestPath, dataSources?.[serviceName]);
160
+ }
161
+ const serviceSettings = dataSources?.[serviceName]?.settings;
162
+ // Check for linked backend annotations and delete if found.
163
+ if (serviceSettings?.annotations && serviceSettings.annotations.length > 0) {
164
+ removeAnnotations(fs, manifestPath, serviceSettings.annotations, dataSources);
165
+ }
166
+ // delete dataSource from manifest
167
+ if (dataSources?.[serviceName]) {
168
+ delete dataSources[serviceName];
169
+ }
170
+ const modelsProp = 'sap.ui5';
171
+ // delete models for this service
172
+ const models = manifest?.[modelsProp]?.models;
173
+ if (models) {
174
+ for (const modelKey of Object.keys(models)) {
175
+ const modelObj = models[modelKey];
176
+ if (modelObj?.dataSource === serviceName) {
177
+ delete models[modelKey];
178
+ }
179
+ }
180
+ }
181
+ fs.writeJSON(manifestPath, manifest);
182
+ }
183
+ //# sourceMappingURL=delete.js.map
package/dist/index.d.ts CHANGED
@@ -1,17 +1,15 @@
1
1
  import type { Editor } from 'mem-fs-editor';
2
2
  import { getAnnotationNamespaces } from './data';
3
+ import type { ProjectPaths } from './types';
3
4
  import { OdataService, OdataVersion, ServiceType, CdsAnnotationsInfo, EdmxAnnotationsInfo, NamespaceAlias } from './types';
4
5
  /**
5
6
  * Try finding a package.json and a ui5.yaml for the given project by looking upwards in the folder hierachy.
6
7
  *
7
8
  * @param {string} basePath - the root path of an existing UI5 application
8
9
  * @param {Editor} [fs] - the memfs editor instance
9
- * @returns an object with the optional locations of the package.json and ui5.yaml
10
+ * @returns an object with the optional locations of the package.json and ui5.yaml, ui5-local.yaml, ui5-mock.yaml
10
11
  */
11
- export declare function findProjectFiles(basePath: string, fs: Editor): Promise<{
12
- packageJson?: string;
13
- ui5Yaml?: string;
14
- }>;
12
+ export declare function findProjectFiles(basePath: string, fs: Editor): Promise<ProjectPaths>;
15
13
  /**
16
14
  * Writes the odata service related file updates to an existing UI5 project specified by the base path.
17
15
  *
@@ -22,6 +20,33 @@ export declare function findProjectFiles(basePath: string, fs: Editor): Promise<
22
20
  * @returns {Promise<Editor>} the updated memfs editor instance
23
21
  */
24
22
  declare function generate(basePath: string, service: OdataService, fs?: Editor): Promise<Editor>;
25
- export { generate, OdataVersion, OdataService, ServiceType, EdmxAnnotationsInfo, CdsAnnotationsInfo };
23
+ /**
24
+ * Removes service related data from project files for an existing UI5 project specified by the base path.
25
+ * Works as follow:
26
+ * 1. Service is removed from manifest.
27
+ * If service type is EDMX:
28
+ * 2. ui5.yaml
29
+ * - backend data of the service is removed from fiori-tools-proxy middleware
30
+ * 3. ui5-local.yaml
31
+ * - backend data of the service is removed from fiori-tools-proxy middleware
32
+ * - service is removed from mockserver middleware
33
+ * 4. ui5-mock.yaml
34
+ * - service is removed from mockserver middleware
35
+ * If service type is CDS:
36
+ * 2. annotations of the service are removed from CDS files.
37
+ *
38
+ * @param {string} basePath - the root path of an existing UI5 application
39
+ * @param {OdataService} service - the OData service instance
40
+ * @param {string} service.name - name of the OData service instance
41
+ * @param {string} service.path - path of the OData service instance
42
+ * @param {string} service.url - url of the OData service instance
43
+ * @param {ServiceType} service.type - type of the OData service instance
44
+ * @param {OdataService['annotations']} service.annotations - services annotations (EDMX or CDS)
45
+ * @param {Editor} [fs] - the memfs editor instance
46
+ * @throws {Error} - if required UI5 project files are not found
47
+ * @returns {Promise<Editor>} the updated memfs editor instance
48
+ */
49
+ declare function remove(basePath: string, service: Required<Pick<OdataService, 'name' | 'path' | 'url' | 'type' | 'annotations'>>, fs?: Editor): Promise<Editor>;
50
+ export { generate, remove, OdataVersion, OdataService, ServiceType, EdmxAnnotationsInfo, CdsAnnotationsInfo };
26
51
  export { getAnnotationNamespaces, NamespaceAlias };
27
52
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getAnnotationNamespaces = exports.ServiceType = exports.OdataVersion = void 0;
7
7
  exports.findProjectFiles = findProjectFiles;
8
8
  exports.generate = generate;
9
+ exports.remove = remove;
9
10
  const path_1 = require("path");
10
11
  const mem_fs_1 = require("mem-fs");
11
12
  const mem_fs_editor_1 = require("mem-fs-editor");
@@ -20,6 +21,7 @@ Object.defineProperty(exports, "OdataVersion", { enumerable: true, get: function
20
21
  Object.defineProperty(exports, "ServiceType", { enumerable: true, get: function () { return types_1.ServiceType; } });
21
22
  const project_access_1 = require("@sap-ux/project-access");
22
23
  const mockserver_config_writer_1 = require("@sap-ux/mockserver-config-writer");
24
+ const delete_1 = require("./delete");
23
25
  /**
24
26
  * Ensures the existence of the given files in the provided base path. If a file in the provided list does not exit, an error would be thrown.
25
27
  *
@@ -39,12 +41,12 @@ function ensureExists(basePath, files, fs) {
39
41
  *
40
42
  * @param {string} basePath - the root path of an existing UI5 application
41
43
  * @param {Editor} [fs] - the memfs editor instance
42
- * @returns an object with the optional locations of the package.json and ui5.yaml
44
+ * @returns an object with the optional locations of the package.json and ui5.yaml, ui5-local.yaml, ui5-mock.yaml
43
45
  */
44
46
  async function findProjectFiles(basePath, fs) {
45
47
  const files = {};
46
48
  const parts = basePath.split(path_1.sep);
47
- while (parts.length > 0 && (!files.packageJson || !files.ui5Yaml)) {
49
+ while (parts.length > 0 && (!files.packageJson || !files.ui5Yaml || !files.ui5LocalYaml || !files.ui5MockYaml)) {
48
50
  const path = parts.join(path_1.sep);
49
51
  if (!files.packageJson && fs.exists((0, path_1.join)(path, 'package.json'))) {
50
52
  files.packageJson = (0, path_1.join)(path, 'package.json');
@@ -52,10 +54,150 @@ async function findProjectFiles(basePath, fs) {
52
54
  if (!files.ui5Yaml && fs.exists((0, path_1.join)(path, 'ui5.yaml'))) {
53
55
  files.ui5Yaml = (0, path_1.join)(path, 'ui5.yaml');
54
56
  }
57
+ if (!files.ui5LocalYaml && fs.exists((0, path_1.join)(path, 'ui5-local.yaml'))) {
58
+ files.ui5LocalYaml = (0, path_1.join)(path, 'ui5-local.yaml');
59
+ }
60
+ if (!files.ui5MockYaml && fs.exists((0, path_1.join)(path, 'ui5-mock.yaml'))) {
61
+ files.ui5MockYaml = (0, path_1.join)(path, 'ui5-mock.yaml');
62
+ }
55
63
  parts.pop();
56
64
  }
57
65
  return files;
58
66
  }
67
+ /**
68
+ * Generates mockserver middleware config for ui5-local.yaml file based on ui5-mock.yaml.
69
+ *
70
+ * @param {Editor} fs - the memfs editor instance
71
+ * @param {OdataService} ui5YamlPath - path pointing to the ui5.yaml file
72
+ * @param {UI5Config} ui5LocalConfigPath - ui5-local.yaml configuration
73
+ * @param {string} ui5LocalConfig - path pointing to the ui5-local.yaml file
74
+ * @returns {Promise<Editor>} the updated memfs editor instance
75
+ */
76
+ async function generateMockserverMiddlewareBasedOnUi5MockYaml(fs, ui5YamlPath, ui5LocalConfigPath, ui5LocalConfig) {
77
+ // Update ui5-local.yaml with mockserver middleware from ui5-mock.yaml
78
+ const ui5MockYamlPath = (0, path_1.join)((0, path_1.dirname)(ui5YamlPath), 'ui5-mock.yaml');
79
+ const ui5MockYamlConfig = await ui5_config_1.UI5Config.newInstance(fs.read(ui5MockYamlPath));
80
+ const mockserverMiddlewareFromUi5Mock = ui5MockYamlConfig.findCustomMiddleware('sap-fe-mockserver');
81
+ if (ui5LocalConfigPath && fs.exists(ui5LocalConfigPath) && ui5LocalConfig && mockserverMiddlewareFromUi5Mock) {
82
+ ui5LocalConfig.updateCustomMiddleware(mockserverMiddlewareFromUi5Mock);
83
+ }
84
+ }
85
+ /**
86
+ * Extends backend middleware for UI5Config with service data.
87
+ *
88
+ * @param {Editor} fs - the memfs editor instance
89
+ * @param {OdataService} service - the OData service instance data
90
+ * @param {UI5Config} ui5Config - UI5 configuration
91
+ * @param {string} ui5ConfigPath - path to the YAML config file
92
+ * @throws {Error} - if required UI5 project files are not found
93
+ */
94
+ function extendBackendMiddleware(fs, service, ui5Config, ui5ConfigPath) {
95
+ try {
96
+ ui5Config.addBackendToFioriToolsProxydMiddleware(service.previewSettings);
97
+ }
98
+ catch (error) {
99
+ if ((error instanceof ui5_config_1.YAMLError && error.code === ui5_config_1.yamlErrorCode.nodeNotFound) ||
100
+ error.message === 'Could not find fiori-tools-proxy') {
101
+ ui5Config.addFioriToolsProxydMiddleware({
102
+ backend: [service.previewSettings],
103
+ ignoreCertError: service.ignoreCertError
104
+ });
105
+ }
106
+ else {
107
+ throw error;
108
+ }
109
+ }
110
+ fs.write(ui5ConfigPath, ui5Config.toString());
111
+ }
112
+ /**
113
+ * Returns all paths of the EDMX service annotations.
114
+ *
115
+ * @param {OdataService} edmxAnnotations - EDMX OData service annotations.
116
+ * @returns {string} annotation paths.
117
+ */
118
+ function getEDMXAnnotationPaths(edmxAnnotations) {
119
+ const emdxAnnotationsPaths = [];
120
+ if (Array.isArray(edmxAnnotations)) {
121
+ edmxAnnotations.forEach((annotation) => {
122
+ const technicalName = encodeURIComponent(annotation.technicalName);
123
+ emdxAnnotationsPaths.push(`/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='${technicalName}',Version='0001')/$value/` // This is how annotation paths are stored in manifest for ODataAnnotations
124
+ );
125
+ });
126
+ }
127
+ else {
128
+ const technicalName = encodeURIComponent(edmxAnnotations.technicalName);
129
+ emdxAnnotationsPaths.push(`/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='${technicalName}',Version='0001')/$value/`);
130
+ }
131
+ return emdxAnnotationsPaths;
132
+ }
133
+ /**
134
+ * Writes local copies of metadata and annotations.
135
+ *
136
+ * @param {Editor} fs - the memfs editor instance
137
+ * @param {string} basePath - the root path of an existing UI5 application
138
+ * @param {string} webappPath - the webapp path of an existing UI5 application
139
+ * @param {string} templateRoot - path to the file templates
140
+ * @param {OdataService} service - the OData service instance with EDMX type
141
+ */
142
+ async function writeLocalServiceFiles(fs, basePath, webappPath, templateRoot, service) {
143
+ // mainService should be used in case there is no name defined for service
144
+ fs.write((0, path_1.join)(webappPath, 'localService', service.name ?? 'mainService', 'metadata.xml'), (0, prettify_xml_1.default)(service.metadata, { indent: 4 }));
145
+ // Adds local annotations to datasources section of manifest.json and writes the annotations file
146
+ if (service.localAnnotationsName) {
147
+ const namespaces = (0, data_1.getAnnotationNamespaces)(service);
148
+ fs.copyTpl((0, path_1.join)(templateRoot, 'add', 'annotation.xml'), (0, path_1.join)(basePath, 'webapp', 'annotations', `${service.localAnnotationsName}.xml`), { ...service, namespaces });
149
+ }
150
+ }
151
+ /**
152
+ * Writes EDMX service data to ui5.yaml, ui5-mock.yaml, ui5-local.yaml, package.json and annotations xml files.
153
+ *
154
+ * @param {Editor} fs - the memfs editor instance
155
+ * @param {string} basePath - the root path of an existing UI5 application
156
+ * @param {ProjectPaths} paths - locations of the package.json and ui5.yaml, ui5-local.yaml, ui5-mock.yaml
157
+ * @param {string} templateRoot - path to the file templates
158
+ * @param {OdataService} service - the OData service instance with EDMX type
159
+ */
160
+ async function writeEDMXServiceFiles(fs, basePath, paths, templateRoot, service) {
161
+ let ui5Config;
162
+ let ui5LocalConfig;
163
+ let ui5MockConfig;
164
+ if (paths.ui5Yaml) {
165
+ ui5Config = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5Yaml));
166
+ // Update ui5.yaml with backend middleware
167
+ extendBackendMiddleware(fs, service, ui5Config, paths.ui5Yaml);
168
+ // Update ui5-local.yaml with backend middleware
169
+ if (paths.ui5LocalYaml) {
170
+ ui5LocalConfig = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5LocalYaml));
171
+ extendBackendMiddleware(fs, service, ui5LocalConfig, paths.ui5LocalYaml);
172
+ }
173
+ }
174
+ if (service.metadata) {
175
+ const webappPath = await (0, project_access_1.getWebappPath)(basePath, fs);
176
+ if (paths.ui5Yaml && ui5Config) {
177
+ const config = {
178
+ webappPath: webappPath
179
+ };
180
+ // Generate mockserver middleware for ui5-mock.yaml
181
+ await (0, mockserver_config_writer_1.generateMockserverConfig)(basePath, config, fs);
182
+ // Update ui5-local.yaml with mockserver middleware from newly created/updated ui5-mock.yaml
183
+ await generateMockserverMiddlewareBasedOnUi5MockYaml(fs, paths.ui5Yaml, paths.ui5LocalYaml, ui5LocalConfig);
184
+ // Update ui5-mock.yaml with backend middleware
185
+ if (paths.ui5MockYaml) {
186
+ ui5MockConfig = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5MockYaml));
187
+ extendBackendMiddleware(fs, service, ui5MockConfig, paths.ui5MockYaml);
188
+ }
189
+ }
190
+ await writeLocalServiceFiles(fs, basePath, webappPath, templateRoot, service);
191
+ }
192
+ if (paths.packageJson && paths.ui5Yaml) {
193
+ (0, updates_1.updatePackageJson)(paths.packageJson, fs, !!service.metadata);
194
+ }
195
+ if (paths.ui5LocalYaml && ui5LocalConfig) {
196
+ // write ui5 local yaml if service type is not CDS
197
+ fs.write(paths.ui5LocalYaml, ui5LocalConfig.toString());
198
+ }
199
+ (0, updates_1.writeAnnotationXmlFiles)(fs, basePath, service.name ?? 'mainService', service.annotations);
200
+ }
59
201
  /**
60
202
  * Writes the odata service related file updates to an existing UI5 project specified by the base path.
61
203
  *
@@ -71,84 +213,91 @@ async function generate(basePath, service, fs) {
71
213
  }
72
214
  const paths = await findProjectFiles(basePath, fs);
73
215
  ensureExists(basePath, ['webapp/manifest.json'], fs);
74
- (0, data_1.enhanceData)(service);
75
- // set isServiceTypeEdmx true if service is EDMX
216
+ await (0, data_1.enhanceData)(basePath, service, fs);
217
+ // Set isServiceTypeEdmx true if service is EDMX
76
218
  const isServiceTypeEdmx = service.type === types_1.ServiceType.EDMX;
77
- // merge content into existing files
219
+ // Prepare template folder for manifest and xml updates
78
220
  const templateRoot = (0, path_1.join)(__dirname, '../templates');
79
- // update cds files with annotations only if service type is CDS and annotations are provided
80
- if (!isServiceTypeEdmx && service.annotations) {
221
+ // Update manifest.json
222
+ (0, updates_1.updateManifest)(basePath, service, fs, templateRoot);
223
+ // Dont extend backend and mockserver middlewares if service type is CDS
224
+ if (isServiceTypeEdmx) {
225
+ await writeEDMXServiceFiles(fs, basePath, paths, templateRoot, service);
226
+ }
227
+ else if (!isServiceTypeEdmx && service.annotations) {
228
+ // Update cds files with annotations only if service type is CDS and annotations are provided
81
229
  await (0, updates_1.updateCdsFilesWithAnnotations)(service.annotations, fs);
82
230
  }
83
- // manifest.json
84
- (0, updates_1.updateManifest)(basePath, service, fs, templateRoot);
85
- // update ui5.yaml if it exists
231
+ return fs;
232
+ }
233
+ /**
234
+ * Removes service related data from project files for an existing UI5 project specified by the base path.
235
+ * Works as follow:
236
+ * 1. Service is removed from manifest.
237
+ * If service type is EDMX:
238
+ * 2. ui5.yaml
239
+ * - backend data of the service is removed from fiori-tools-proxy middleware
240
+ * 3. ui5-local.yaml
241
+ * - backend data of the service is removed from fiori-tools-proxy middleware
242
+ * - service is removed from mockserver middleware
243
+ * 4. ui5-mock.yaml
244
+ * - service is removed from mockserver middleware
245
+ * If service type is CDS:
246
+ * 2. annotations of the service are removed from CDS files.
247
+ *
248
+ * @param {string} basePath - the root path of an existing UI5 application
249
+ * @param {OdataService} service - the OData service instance
250
+ * @param {string} service.name - name of the OData service instance
251
+ * @param {string} service.path - path of the OData service instance
252
+ * @param {string} service.url - url of the OData service instance
253
+ * @param {ServiceType} service.type - type of the OData service instance
254
+ * @param {OdataService['annotations']} service.annotations - services annotations (EDMX or CDS)
255
+ * @param {Editor} [fs] - the memfs editor instance
256
+ * @throws {Error} - if required UI5 project files are not found
257
+ * @returns {Promise<Editor>} the updated memfs editor instance
258
+ */
259
+ async function remove(basePath, service, fs) {
260
+ if (!fs) {
261
+ fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
262
+ }
86
263
  let ui5Config;
87
264
  let ui5LocalConfig;
88
- let ui5LocalConfigPath;
89
- if (isServiceTypeEdmx && paths.ui5Yaml) {
90
- // Dont extend backend middlewares if service type is CDS.
91
- ui5Config = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5Yaml));
92
- try {
93
- ui5Config.addBackendToFioriToolsProxydMiddleware(service.previewSettings);
265
+ let ui5MockConfig;
266
+ const paths = await findProjectFiles(basePath, fs);
267
+ // Delete service and it's annotations from manifest
268
+ (0, delete_1.deleteServiceFromManifest)(basePath, service.name, fs);
269
+ const isServiceTypeEdmx = service.type === types_1.ServiceType.EDMX;
270
+ // Remove service related data from middlewares for EDMX services
271
+ if (isServiceTypeEdmx) {
272
+ // Delete service data from manifest.json
273
+ if (paths.ui5Yaml) {
274
+ ui5Config = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5Yaml));
275
+ // Delete service backend from fiori-tools-proxy middleware config
276
+ ui5Config.removeBackendFromFioriToolsProxydMiddleware(service.url);
277
+ fs.write(paths.ui5Yaml, ui5Config.toString());
94
278
  }
95
- catch (error) {
96
- if (error instanceof ui5_config_1.YAMLError && error.code === ui5_config_1.yamlErrorCode.nodeNotFound) {
97
- ui5Config.addFioriToolsProxydMiddleware({
98
- backend: [service.previewSettings],
99
- ignoreCertError: service.ignoreCertError
100
- });
101
- }
102
- else {
103
- throw error;
104
- }
279
+ const serviceAnnotationPaths = getEDMXAnnotationPaths(service.annotations);
280
+ if (paths.ui5LocalYaml) {
281
+ ui5LocalConfig = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5LocalYaml));
282
+ // Delete service backend from fiori-tools-proxy middleware config
283
+ ui5LocalConfig.removeBackendFromFioriToolsProxydMiddleware(service.url);
284
+ // Delete service from mockserver middleware config
285
+ ui5LocalConfig.removeServiceFromMockServerMiddleware(service.path, serviceAnnotationPaths);
286
+ fs.write(paths.ui5LocalYaml, ui5LocalConfig.toString());
105
287
  }
106
- fs.write(paths.ui5Yaml, ui5Config.toString());
107
- // ui5-local.yaml
108
- ui5LocalConfigPath = (0, path_1.join)((0, path_1.dirname)(paths.ui5Yaml), 'ui5-local.yaml');
109
- if (fs.exists(ui5LocalConfigPath)) {
110
- ui5LocalConfig = await ui5_config_1.UI5Config.newInstance(fs.read(ui5LocalConfigPath));
111
- ui5LocalConfig.addFioriToolsProxydMiddleware({
112
- backend: [service.previewSettings],
113
- ignoreCertError: service.ignoreCertError
114
- });
288
+ if (paths.ui5MockYaml) {
289
+ ui5MockConfig = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5MockYaml));
290
+ // Delete service backend from fiori-tools-proxy middleware config
291
+ ui5MockConfig.removeBackendFromFioriToolsProxydMiddleware(service.url);
292
+ // Delete service from mockserver config
293
+ ui5MockConfig.removeServiceFromMockServerMiddleware(service.path, serviceAnnotationPaths);
294
+ fs.write(paths.ui5MockYaml, ui5MockConfig.toString());
115
295
  }
296
+ (0, delete_1.removeAnnotationXmlFiles)(fs, basePath, service.name ?? 'mainService', service.annotations);
116
297
  }
117
- // Add mockserver entries
118
- if (isServiceTypeEdmx && service.metadata) {
119
- // mockserver entries are not required if service type is CDS
120
- // copy existing `ui5.yaml` as starting point for ui5-mock.yaml
121
- if (paths.ui5Yaml && ui5Config) {
122
- const webappPath = await (0, project_access_1.getWebappPath)(basePath, fs);
123
- const config = {
124
- webappPath: webappPath,
125
- ui5MockYamlConfig: { path: service.path }
126
- };
127
- await (0, mockserver_config_writer_1.generateMockserverConfig)(basePath, config, fs);
128
- // add mockserver middleware to ui5-local.yaml
129
- if (ui5LocalConfig) {
130
- ui5LocalConfig.addMockServerMiddleware(service.path);
131
- }
132
- }
133
- // create local copy of metadata and annotations
134
- fs.write((0, path_1.join)(basePath, 'webapp', 'localService', 'metadata.xml'), (0, prettify_xml_1.default)(service.metadata, { indent: 4 }));
135
- // Adds local annotations to datasources section of manifest.json and writes the annotations file
136
- if (service.localAnnotationsName) {
137
- const namespaces = (0, data_1.getAnnotationNamespaces)(service);
138
- fs.copyTpl((0, path_1.join)(templateRoot, 'add', 'annotation.xml'), (0, path_1.join)(basePath, 'webapp', 'annotations', `${service.localAnnotationsName}.xml`), { ...service, namespaces });
139
- }
140
- }
141
- // update package.json for non-cap applications
142
- if (isServiceTypeEdmx && paths.packageJson && paths.ui5Yaml) {
143
- (0, updates_1.updatePackageJson)(paths.packageJson, fs, !!service.metadata);
144
- }
145
- if (isServiceTypeEdmx && ui5LocalConfigPath && ui5LocalConfig) {
146
- // write ui5 local yaml if service type is not CDS
147
- fs.write(ui5LocalConfigPath, ui5LocalConfig.toString());
148
- }
149
- // Write annotation xml if annotations are provided and service type is EDMX
150
- if (isServiceTypeEdmx) {
151
- (0, updates_1.writeAnnotationXmlFiles)(fs, basePath, service);
298
+ else {
299
+ // Remove annotations from CDS files based on annotations info
300
+ await (0, delete_1.removeAnnotationsFromCDSFiles)(service.annotations, fs);
152
301
  }
153
302
  return fs;
154
303
  }
package/dist/types.d.ts CHANGED
@@ -65,7 +65,7 @@ export interface OdataService {
65
65
  /**
66
66
  * Annotations can either be EDMX annotations or CDS annotations.
67
67
  */
68
- annotations?: EdmxAnnotationsInfo | CdsAnnotationsInfo;
68
+ annotations?: EdmxAnnotationsInfo | EdmxAnnotationsInfo[] | CdsAnnotationsInfo | CdsAnnotationsInfo[];
69
69
  localAnnotationsName?: string;
70
70
  previewSettings?: Partial<ProxyBackend>;
71
71
  /**
@@ -73,4 +73,13 @@ export interface OdataService {
73
73
  */
74
74
  ignoreCertError?: boolean;
75
75
  }
76
+ export type EdmxOdataService = Omit<OdataService, 'annotations'> & {
77
+ annotations: EdmxAnnotationsInfo | EdmxAnnotationsInfo[];
78
+ };
79
+ export interface ProjectPaths {
80
+ packageJson?: string;
81
+ ui5Yaml?: string;
82
+ ui5LocalYaml?: string;
83
+ ui5MockYaml?: string;
84
+ }
76
85
  //# sourceMappingURL=types.d.ts.map
package/dist/updates.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Editor } from 'mem-fs-editor';
2
- import type { OdataService, CdsAnnotationsInfo } from './types';
2
+ import type { OdataService, CdsAnnotationsInfo, EdmxAnnotationsInfo } from './types';
3
3
  /**
4
4
  * Internal function that updates the manifest.json based on the given service configuration.
5
5
  *
@@ -10,13 +10,14 @@ import type { OdataService, CdsAnnotationsInfo } from './types';
10
10
  */
11
11
  export declare function updateManifest(basePath: string, service: OdataService, fs: Editor, templateRoot: string): void;
12
12
  /**
13
- * Writes annotation XML files.
13
+ * Writes annotation XML files for EDMX service annotations.
14
14
  *
15
15
  * @param {Editor} fs - The memfs editor instance.
16
16
  * @param {string} basePath - The base path of the project.
17
- * @param {OdataService} service - The OData service information.
17
+ * @param {string} serviceName - Name of The OData service.
18
+ * @param {OdataService} edmxAnnotations - The OData service annotations.
18
19
  */
19
- export declare function writeAnnotationXmlFiles(fs: Editor, basePath: string, service: OdataService): void;
20
+ export declare function writeAnnotationXmlFiles(fs: Editor, basePath: string, serviceName: string, edmxAnnotations: EdmxAnnotationsInfo | EdmxAnnotationsInfo[]): void;
20
21
  /**
21
22
  * Updates cds files with the provided annotations.
22
23
  * This function takes cds annotations and an Editor instance,
@@ -26,7 +27,7 @@ export declare function writeAnnotationXmlFiles(fs: Editor, basePath: string, se
26
27
  * @param {Editor} fs - The memfs editor instance
27
28
  * @returns {Promise<void>} A promise that resolves when the cds files have been updated.
28
29
  */
29
- export declare function updateCdsFilesWithAnnotations(annotations: CdsAnnotationsInfo, fs: Editor): Promise<void>;
30
+ export declare function updateCdsFilesWithAnnotations(annotations: CdsAnnotationsInfo | CdsAnnotationsInfo[], fs: Editor): Promise<void>;
30
31
  /**
31
32
  * Update the package.json with the required middlewares.
32
33
  *
package/dist/updates.js CHANGED
@@ -1,27 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
@@ -31,7 +8,7 @@ exports.writeAnnotationXmlFiles = writeAnnotationXmlFiles;
31
8
  exports.updateCdsFilesWithAnnotations = updateCdsFilesWithAnnotations;
32
9
  exports.updatePackageJson = updatePackageJson;
33
10
  const ejs_1 = require("ejs");
34
- const path_1 = __importStar(require("path"));
11
+ const path_1 = require("path");
35
12
  const i18n_1 = require("./i18n");
36
13
  const semver_1 = __importDefault(require("semver"));
37
14
  const prettify_xml_1 = __importDefault(require("prettify-xml"));
@@ -71,7 +48,7 @@ function updateManifest(basePath, service, fs, templateRoot) {
71
48
  */
72
49
  async function updateCdsIndexOrServiceFile(fs, annotations) {
73
50
  const dirPath = (0, path_1.join)(annotations.projectName, 'annotations');
74
- const annotationPath = path_1.default.normalize(dirPath).split(/[\\/]/g).join(path_1.default.posix.sep);
51
+ const annotationPath = (0, path_1.normalize)(dirPath).split(/[\\/]/g).join(path_1.posix.sep);
75
52
  const annotationConfig = `\nusing from './${annotationPath}';`;
76
53
  // get index and service file paths
77
54
  const indexFilePath = (0, path_1.join)(annotations.projectPath, annotations.appPath ?? '', 'index.cds');
@@ -88,17 +65,25 @@ async function updateCdsIndexOrServiceFile(fs, annotations) {
88
65
  }
89
66
  }
90
67
  /**
91
- * Writes annotation XML files.
68
+ * Writes annotation XML files for EDMX service annotations.
92
69
  *
93
70
  * @param {Editor} fs - The memfs editor instance.
94
71
  * @param {string} basePath - The base path of the project.
95
- * @param {OdataService} service - The OData service information.
72
+ * @param {string} serviceName - Name of The OData service.
73
+ * @param {OdataService} edmxAnnotations - The OData service annotations.
96
74
  */
97
- function writeAnnotationXmlFiles(fs, basePath, service) {
75
+ function writeAnnotationXmlFiles(fs, basePath, serviceName, edmxAnnotations) {
98
76
  // Write annotation xml if annotations are provided and service type is EDMX
99
- const annotations = service.annotations;
100
- if (annotations?.xml) {
101
- fs.write((0, path_1.join)(basePath, 'webapp', 'localService', `${annotations.technicalName}.xml`), (0, prettify_xml_1.default)(annotations.xml, { indent: 4 }));
77
+ if (Array.isArray(edmxAnnotations)) {
78
+ for (const annotationName in edmxAnnotations) {
79
+ const annotation = edmxAnnotations[annotationName];
80
+ if (annotation?.xml) {
81
+ fs.write((0, path_1.join)(basePath, 'webapp', 'localService', serviceName, `${annotation.technicalName}.xml`), (0, prettify_xml_1.default)(annotation.xml, { indent: 4 }));
82
+ }
83
+ }
84
+ }
85
+ else if (edmxAnnotations?.xml) {
86
+ fs.write((0, path_1.join)(basePath, 'webapp', 'localService', serviceName, `${edmxAnnotations.technicalName}.xml`), (0, prettify_xml_1.default)(edmxAnnotations.xml, { indent: 4 }));
102
87
  }
103
88
  }
104
89
  /**
@@ -111,10 +96,26 @@ function writeAnnotationXmlFiles(fs, basePath, service) {
111
96
  * @returns {Promise<void>} A promise that resolves when the cds files have been updated.
112
97
  */
113
98
  async function updateCdsFilesWithAnnotations(annotations, fs) {
114
- const annotationCdsPath = (0, path_1.join)(annotations.projectPath, annotations.appPath ?? '', annotations.projectName, 'annotations.cds');
115
- // write into annotations.cds file
116
- fs.write(annotationCdsPath, annotations.cdsFileContents);
117
- await updateCdsIndexOrServiceFile(fs, annotations);
99
+ if (Array.isArray(annotations)) {
100
+ for (const annotationName in annotations) {
101
+ const annotation = annotations[annotationName];
102
+ const annotationCdsPath = (0, path_1.join)(annotation.projectPath, annotation.appPath ?? '', annotation.projectName, 'annotations.cds');
103
+ // write into annotations.cds file
104
+ if (fs.exists(annotationCdsPath)) {
105
+ fs.append(annotationCdsPath, annotation.cdsFileContents);
106
+ }
107
+ else {
108
+ fs.write(annotationCdsPath, annotation.cdsFileContents);
109
+ }
110
+ await updateCdsIndexOrServiceFile(fs, annotation);
111
+ }
112
+ }
113
+ else {
114
+ const annotationCdsPath = (0, path_1.join)(annotations.projectPath, annotations.appPath ?? '', annotations.projectName, 'annotations.cds');
115
+ // write into annotations.cds file
116
+ fs.write(annotationCdsPath, annotations.cdsFileContents);
117
+ await updateCdsIndexOrServiceFile(fs, annotations);
118
+ }
118
119
  }
119
120
  /**
120
121
  * Determines model settings based on the UI5 version.
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.23.2",
12
+ "version": "0.24.0",
13
13
  "license": "Apache-2.0",
14
14
  "main": "dist/index.js",
15
15
  "files": [
@@ -27,8 +27,8 @@
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.6.6",
31
- "@sap-ux/ui5-config": "0.25.1"
30
+ "@sap-ux/mockserver-config-writer": "0.7.0",
31
+ "@sap-ux/ui5-config": "0.26.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/ejs": "3.1.2",
@@ -38,7 +38,7 @@
38
38
  "@types/semver": "7.5.2",
39
39
  "fs-extra": "10.0.0",
40
40
  "lodash": "4.17.21",
41
- "@sap-ux/project-access": "1.28.6"
41
+ "@sap-ux/project-access": "1.28.8"
42
42
  },
43
43
  "engines": {
44
44
  "node": ">=18.x"
@@ -5,22 +5,52 @@
5
5
  "uri": "<%=path%>",
6
6
  "type": "OData",
7
7
  "settings": {
8
- "annotations": [<%if (locals.annotations && annotations.technicalName) {%>
9
- "<%- annotations.name %>"<% if (locals.localAnnotationsName) {%>,<%}%><% } %><% if (locals.localAnnotationsName) { %>
10
- "<%- localAnnotationsName %>"<% } %>
11
- ]<% if (locals.metadata) { %>,
12
- "localUri": "localService/metadata.xml" <% } %><%if (version === '4') {%>,
8
+ "annotations": [
9
+ <% if (locals.annotations && typeof annotations !== 'undefined') { %>
10
+ <% if (annotations.length) { %>
11
+ <% annotations.forEach(function(annotation, index) { %>
12
+ <% if (annotation.technicalName) { %>
13
+ "<%= annotation.name %>"<% if (index < annotations.length - 1) { %>,<% } %>
14
+ <% } %>
15
+ <% }); %>
16
+ <% } else if (annotations.technicalName) { %>
17
+ "<%= annotations.name %>"
18
+ <% } %>
19
+ <% if (locals.localAnnotationsName) {%>,<%}%><% } %>
20
+ <% if (locals.localAnnotationsName) { %>
21
+ "<%- localAnnotationsName %>"
22
+ <% } %>
23
+ ]
24
+ <% if (locals.metadata) { %>,
25
+ "localUri": "localService/<%- name %>/metadata.xml" <% } %><%if (version === '4') {%>,
13
26
  "odataVersion": "4.0"<% } %><%if (version === '2') {%>,
14
27
  "odataVersion": "2.0"<% } %>
15
28
  }
16
- }<%if (locals.annotations && annotations.technicalName) {%>,
17
- "<%- annotations.name %>": {
18
- "uri": "/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='<%- encodeURIComponent(annotations.technicalName) %>',Version='0001')/$value/",
19
- "type": "ODataAnnotation",
20
- "settings": {
21
- "localUri": "localService/<%- annotations.technicalName %>.xml"
22
- }
23
- }<% } %><% if (locals.localAnnotationsName) { %>,
29
+ }
30
+ <% if (locals.annotations && typeof annotations !== 'undefined') { %>
31
+ <% if (annotations.length && annotations.length > 0) { %>
32
+ <% annotations.forEach(function(annotation, index) { %>
33
+ <% if (annotation.technicalName) { %>,
34
+ "<%= annotation.name %>": {
35
+ "uri": "/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='<%- encodeURIComponent(annotation.technicalName) %>',Version='0001')/$value/",
36
+ "type": "ODataAnnotation",
37
+ "settings": {
38
+ "localUri": "localService/<%- name %>/<%- annotation.technicalName %>.xml"
39
+ }
40
+ }
41
+ <% } %>
42
+ <% }); %>
43
+ <% } else if (annotations.technicalName) { %>,
44
+ "<%= annotations.name %>": {
45
+ "uri": "/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='<%- encodeURIComponent(annotations.technicalName) %>',Version='0001')/$value/",
46
+ "type": "ODataAnnotation",
47
+ "settings": {
48
+ "localUri": "localService/<%- name %>/<%- annotations.technicalName %>.xml"
49
+ }
50
+ }
51
+ <% } %>
52
+ <% } %>
53
+ <% if (locals.localAnnotationsName) { %>,
24
54
  "<%- localAnnotationsName %>": {
25
55
  "type": "ODataAnnotation",
26
56
  "uri": "annotations/<%- localAnnotationsName %>.xml",