@ui5/task-adaptation 1.1.1 → 1.1.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.
package/CHANGELOG.md CHANGED
@@ -2,7 +2,10 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
4
4
 
5
- A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task-adaptation/compare/v1.1.1...HEAD).
5
+ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task-adaptation/compare/v1.1.2...HEAD).
6
+
7
+ <a name="v1.1.2"></a>
8
+ ## [v1.1.2] - 2024-02-23
6
9
 
7
10
  <a name="v1.1.1"></a>
8
11
  ## [v1.1.1] - 2024-02-16
@@ -76,6 +79,7 @@ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task
76
79
  <a name="v1.0.0"></a>
77
80
  ## v1.0.0 - 2020-12-09
78
81
 
82
+ [v1.1.2]: https://github.com/SAP/ui5-task-adaptation/compare/v1.1.1...v1.1.2
79
83
  [v1.1.1]: https://github.com/SAP/ui5-task-adaptation/compare/v1.1.0...v1.1.1
80
84
  [v1.1.0]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.23...v1.1.0
81
85
  [v1.0.23]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.21...v1.0.23
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![npm version](https://badge.fury.io/js/@ui5%2Ftask-adaptation.svg)](https://badge.fury.io/js/@ui5%2Ftask-adaptation)
5
5
 
6
6
  ## Description
7
- A custom task for [ui5-builder](https://github.com/SAP/ui5-builder) that allows building [UI5 Flexibility adaptation projects](https://help.sap.com/viewer/584e0bcbfd4a4aff91c815cefa0bce2d/Cloud/en-US/019b0c38a6b043d1a66b11d992eed290.html) for SAP BTP, Cloud Foundry environment.
7
+ A custom task for [ui5-builder](https://github.com/SAP/ui5-builder) that allows building SAPUI5 adaptation projects for [SAP S/4HANA Cloud](https://help.sap.com/docs/bas/584e0bcbfd4a4aff91c815cefa0bce2d/6fc4e11a4b1941efa8e37a428d046f8f.html?locale=en-US&state=PRODUCTION&version=Cloud) and [SAP BTP, Cloud Foundry environment](https://help.sap.com/viewer/584e0bcbfd4a4aff91c815cefa0bce2d/Cloud/en-US/019b0c38a6b043d1a66b11d992eed290.html).
8
8
 
9
9
  ## How to obtain support
10
10
  In case you need any support, please create a [GitHub issue](https://github.com/SAP/ui5-task-adaptation/issues).
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const dataSourceOData_1 = require("./dataSourceOData");
4
4
  const dataSourceODataAnnotation_1 = require("./dataSourceODataAnnotation");
5
+ const dataSourceODataAnnotationBeta_1 = require("./dataSourceODataAnnotationBeta");
5
6
  const path_1 = require("path");
6
7
  class DataSourceManager {
7
8
  constructor() {
@@ -29,8 +30,15 @@ class DataSourceManager {
29
30
  }
30
31
  // If ODataAnnotation is in OData annotations, pass metadata url to it
31
32
  for (const [name, dataSource] of Object.entries(dataSourcesJson)) {
32
- if (dataSource.uri?.startsWith("/") && dataSource.type === "ODataAnnotation") {
33
- this.dataSources.push(new dataSourceODataAnnotation_1.default(name, dataSource.uri, dataSource, odataAnnotationMap.get(name)));
33
+ const uri = dataSource.uri;
34
+ const metadataUrl = odataAnnotationMap.get(name);
35
+ if (uri?.startsWith("/") && dataSource.type === "ODataAnnotation") {
36
+ if (config.enableBetaFeatures) {
37
+ this.dataSources.push(new dataSourceODataAnnotationBeta_1.default(name, uri, dataSource, metadataUrl));
38
+ }
39
+ else {
40
+ this.dataSources.push(new dataSourceODataAnnotation_1.default(name, uri, dataSource));
41
+ }
34
42
  }
35
43
  }
36
44
  for (const dataSource of this.dataSources) {
@@ -1,6 +1,6 @@
1
1
  import DataSource from "./dataSource";
2
2
  export default class DataSourceODataAnnotation extends DataSource {
3
3
  private dataSourceJson;
4
- constructor(name: string, uri: string, dataSourceJson: any, metadataUrl?: string);
4
+ constructor(name: string, uri: string, dataSourceJson: any);
5
5
  updateManifest(): void;
6
6
  }
@@ -1,14 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const dataSource_1 = require("./dataSource");
4
- const traverseReferences_1 = require("../transformers/traverseReferences");
5
4
  class DataSourceODataAnnotation extends dataSource_1.default {
6
- constructor(name, uri, dataSourceJson, metadataUrl) {
5
+ constructor(name, uri, dataSourceJson) {
7
6
  super(name, uri);
8
7
  this.dataSourceJson = dataSourceJson;
9
- this.jsonTransformers = [
10
- new traverseReferences_1.default(metadataUrl)
11
- ];
12
8
  }
13
9
  updateManifest() {
14
10
  this.dataSourceJson.uri = this.getFilename();
@@ -0,0 +1,6 @@
1
+ import DataSource from "./dataSource";
2
+ export default class DataSourceODataAnnotation extends DataSource {
3
+ private dataSourceJson;
4
+ constructor(name: string, uri: string, dataSourceJson: any, metadataUrl: string | undefined);
5
+ updateManifest(): void;
6
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const dataSource_1 = require("./dataSource");
4
+ const traverseReferences_1 = require("../transformers/traverseReferences");
5
+ class DataSourceODataAnnotation extends dataSource_1.default {
6
+ constructor(name, uri, dataSourceJson, metadataUrl) {
7
+ super(name, uri);
8
+ this.dataSourceJson = dataSourceJson;
9
+ this.jsonTransformers = [
10
+ new traverseReferences_1.default(metadataUrl)
11
+ ];
12
+ }
13
+ updateManifest() {
14
+ this.dataSourceJson.uri = this.getFilename();
15
+ }
16
+ }
17
+ exports.default = DataSourceODataAnnotation;
18
+ //# sourceMappingURL=dataSourceODataAnnotationBeta.js.map
@@ -1,8 +1,41 @@
1
1
  "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
2
8
  Object.defineProperty(exports, "__esModule", { value: true });
3
9
  const annotationsCacheManager_1 = require("../cache/annotationsCacheManager");
10
+ const serverError_1 = require("../model/serverError");
4
11
  const commonUtil_1 = require("../util/commonUtil");
5
12
  const log = require("@ui5/logger").getLogger("@ui5/task-adaptation::ServiceRequestor");
13
+ function retryOnError(maxRetries) {
14
+ return (_target, _propertyKey, descriptor) => {
15
+ const originalMethod = descriptor.value;
16
+ descriptor.value = async function (...args) {
17
+ let retries = 0;
18
+ while (true) {
19
+ try {
20
+ const result = await originalMethod.apply(this, args);
21
+ return result;
22
+ }
23
+ catch (error) {
24
+ if (error instanceof serverError_1.default) {
25
+ if (retries === maxRetries) {
26
+ throw new Error(`Error occurred: ${error.message}. Please try again if this is a temporary issue. If not, please create a ticket on CA-UI5-ABA-AIDX`);
27
+ }
28
+ retries++;
29
+ }
30
+ else {
31
+ throw new Error(`Failed to fetch annotation by '${args[0]}': ${error.message}`);
32
+ }
33
+ }
34
+ }
35
+ };
36
+ return descriptor;
37
+ };
38
+ }
6
39
  class ServiceRequestor {
7
40
  constructor(configuration, abapRepoManager) {
8
41
  this.abapRepoManager = abapRepoManager;
@@ -16,25 +49,23 @@ class ServiceRequestor {
16
49
  }
17
50
  const cacheManager = new annotationsCacheManager_1.default(this.configuration, cacheName);
18
51
  log.verbose(`Getting annotation '${cacheName}' ${language} by '${uri}'`);
19
- try {
20
- let files;
21
- if (this.configuration.enableAnnotationCache) {
22
- files = await cacheManager.getFiles(() => this.abapRepoManager.getAnnotationMetadata(uri), () => this.abapRepoManager.downloadAnnotationFile(uri));
23
- }
24
- else {
25
- files = await this.abapRepoManager.downloadAnnotationFile(uri);
26
- }
27
- if (!files || files.size === 0) {
28
- throw new Error(`No files were fetched for '${name}' by '${uri}'`);
29
- }
30
- const xml = [...files][0][1];
31
- (0, commonUtil_1.writeTempAnnotations)(this.configuration, name, language, xml);
32
- return xml;
52
+ let files;
53
+ if (this.configuration.enableAnnotationCache) {
54
+ files = await cacheManager.getFiles(() => this.abapRepoManager.getAnnotationMetadata(uri), () => this.abapRepoManager.downloadAnnotationFile(uri));
55
+ }
56
+ else {
57
+ files = await this.abapRepoManager.downloadAnnotationFile(uri);
33
58
  }
34
- catch (error) {
35
- throw new Error(`Failed to fetch annotation by '${uri}': ${error.message}`);
59
+ if (!files || files.size === 0) {
60
+ throw new Error(`No files were fetched for '${name}' by '${uri}'`);
36
61
  }
62
+ const xml = [...files][0][1];
63
+ (0, commonUtil_1.writeTempAnnotations)(this.configuration, name, language, xml);
64
+ return xml;
37
65
  }
38
66
  }
67
+ __decorate([
68
+ retryOnError(1)
69
+ ], ServiceRequestor.prototype, "downloadAnnotation", null);
39
70
  exports.default = ServiceRequestor;
40
71
  //# sourceMappingURL=serviceRequestor.js.map
@@ -3,8 +3,7 @@ export default class TraverseReferences implements Transformer {
3
3
  private metadataUrl?;
4
4
  constructor(metadataUrl?: string);
5
5
  transform({ json, language, serviceRequestor, uri: parentUrl }: TransformerInput): Promise<void>;
6
- private isReferenceToMetadata;
7
- static joinUrls(relativeUrl: string, parentUrl: string): any;
6
+ private shouldIgnoreUrl;
8
7
  private mergeAnnotations;
9
8
  private static isTraversable;
10
9
  }
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const metadataJsonUtil_1 = require("../converter/metadataJsonUtil");
4
- const dataSourceODataAnnotation_1 = require("../dataSource/dataSourceODataAnnotation");
5
- const { URI } = require("../../../dist/bundle-odata");
4
+ const dataSourceODataAnnotationBeta_1 = require("../dataSource/dataSourceODataAnnotationBeta");
5
+ const urlUtil_1 = require("../../util/urlUtil");
6
6
  class TraverseReferences {
7
7
  constructor(metadataUrl) {
8
8
  this.metadataUrl = metadataUrl;
@@ -11,13 +11,13 @@ class TraverseReferences {
11
11
  const references = metadataJsonUtil_1.default.getReferences(json).filter(TraverseReferences.isTraversable);
12
12
  const promises = [];
13
13
  for (const { includes, uri: relativeUrl } of references) {
14
- const absoluteUrl = TraverseReferences.joinUrls(relativeUrl, parentUrl);
15
- if (this.isReferenceToMetadata(parentUrl, absoluteUrl, this.metadataUrl)) {
16
- // If ODataAnnotation has reference to metadata, don't traverse
14
+ const absoluteUrl = urlUtil_1.default.join(urlUtil_1.default.getResourcePath(relativeUrl), parentUrl);
15
+ if (this.shouldIgnoreUrl(absoluteUrl, [parentUrl, this.metadataUrl])) {
16
+ // If reference to metadata or its parent, don't traverse
17
17
  continue;
18
18
  }
19
19
  const name = includes[0]?.namespace;
20
- const dataSource = new dataSourceODataAnnotation_1.default(name, absoluteUrl, {}, this.metadataUrl);
20
+ const dataSource = new dataSourceODataAnnotationBeta_1.default(name, absoluteUrl, {}, this.metadataUrl);
21
21
  promises.push(dataSource.downloadAnnotation(language, serviceRequestor)
22
22
  .then(childAnnotation => ({ name, childAnnotation })));
23
23
  }
@@ -27,18 +27,10 @@ class TraverseReferences {
27
27
  }
28
28
  return json;
29
29
  }
30
- isReferenceToMetadata(parentUrl, absoluteUrl, metadataUrl) {
30
+ shouldIgnoreUrl(referenceUrl, urlsToIgnore) {
31
+ const toResourcePath = (url) => urlUtil_1.default.getResourcePath(url);
31
32
  const isEqual = (a, b) => a && b && a.toLowerCase() === b.toLowerCase();
32
- return isEqual(parentUrl, metadataUrl) || isEqual(absoluteUrl, metadataUrl);
33
- }
34
- static joinUrls(relativeUrl, parentUrl) {
35
- // Remove trailing slash, otherwise url join can be incorrect Remove
36
- // trailing slash, otherwise url join can be incorrect Annotation URLs
37
- // defined in manifest might end with .../$value/ or .../$value and both
38
- // are accepted by Gateway and produce the same content with same
39
- // relative URLs. The first case is actually incorrect and we have to
40
- // sanitize the same way as UI5.
41
- return new URI(relativeUrl).absoluteTo(parentUrl.replace(/\/$/, "")).toString();
33
+ return urlsToIgnore.map(toResourcePath).some(url => isEqual(referenceUrl, url));
42
34
  }
43
35
  mergeAnnotations(parentJson, nestedJson) {
44
36
  const parentAnnotations = metadataJsonUtil_1.default.getAnnotations(parentJson);
@@ -59,11 +59,11 @@ class BaseAppManager {
59
59
  return (0, commonUtil_1.removePropertiesExtension)(i18nNode["bundleUrl"]);
60
60
  }
61
61
  static getBaseAppManifest(baseAppFiles) {
62
- let filepath = [...baseAppFiles.keys()].find(filepath => filepath.endsWith("manifest.json"));
63
- if (filepath) {
62
+ const manifestContent = baseAppFiles.get("manifest.json");
63
+ if (manifestContent) {
64
64
  return {
65
- content: JSON.parse(baseAppFiles.get(filepath)),
66
- filepath
65
+ content: JSON.parse(manifestContent),
66
+ filepath: "manifest.json"
67
67
  };
68
68
  }
69
69
  throw new Error("Original application should have manifest.json in root folder");
@@ -118,9 +118,9 @@ class BaseAppManager {
118
118
  }
119
119
  static writeToWorkspace(baseAppFiles, projectNamespace) {
120
120
  const IGNORE_FILES = [
121
- "/manifest-bundle.zip",
122
- "/Component-preload.js",
123
- "/sap-ui-cachebuster-info.json"
121
+ "manifest-bundle.zip",
122
+ "Component-preload.js",
123
+ "sap-ui-cachebuster-info.json"
124
124
  ];
125
125
  const resources = [];
126
126
  for (let filename of baseAppFiles.keys()) {
@@ -0,0 +1,3 @@
1
+ export default class ServerError extends Error {
2
+ constructor(uri: string, error: any);
3
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class ServerError extends Error {
4
+ constructor(uri, error) {
5
+ super(`Request ${uri} failed with Server error: ${error.response.status}${error.response.data ?? ""}`);
6
+ }
7
+ }
8
+ exports.default = ServerError;
9
+ //# sourceMappingURL=serverError.js.map
@@ -6,12 +6,12 @@ const log = require("@ui5/logger").getLogger("@ui5/task-adaptation::AbapRepoMana
6
6
  const REQUEST_OPTIONS_XML = {
7
7
  responseType: "text",
8
8
  headers: {
9
- "Content-Type": "text/xml"
9
+ "Accept": "text/html,application/xhtml+xml,application/xml"
10
10
  }
11
11
  };
12
12
  const REQUEST_OPTIONS_JSON = {
13
13
  headers: {
14
- "Content-Type": "application/json"
14
+ "Accept": "application/json"
15
15
  }
16
16
  };
17
17
  class AbapRepoManager {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const axios_1 = require("axios");
4
4
  const noAuthorizationProvidedError_1 = require("../model/noAuthorizationProvidedError");
5
+ const serverError_1 = require("../model/serverError");
5
6
  class RequestUtil {
6
7
  static async head(url, auth) {
7
8
  return this.request(url, axios_1.default.head, auth);
@@ -24,6 +25,9 @@ class RequestUtil {
24
25
  if (error.response.status === 401) {
25
26
  throw new noAuthorizationProvidedError_1.default(uri);
26
27
  }
28
+ else if (error.response.status >= 500) {
29
+ throw new serverError_1.default(uri, error);
30
+ }
27
31
  else {
28
32
  throw new Error(`Unexpected response received from '${uri}': ${error.response.status} ${error.response.data ?? ""}`);
29
33
  }
@@ -37,7 +37,7 @@ class ResourceUtil {
37
37
  const entryPath = path_1.posix.join(folder, entry);
38
38
  const stats = fs.lstatSync(entryPath);
39
39
  if (stats.isFile() && !exclude.some(filepath => entryPath.endsWith(filepath))) {
40
- const normalized = entryPath.substring(rootFolder.length);
40
+ const normalized = entryPath.substring(rootFolder.length + 1);
41
41
  files.set(normalized, fs.readFileSync(entryPath, { encoding: "utf-8" }));
42
42
  }
43
43
  else if (stats.isDirectory()) {
@@ -0,0 +1,4 @@
1
+ export default class UrlUtil {
2
+ static join(relativeUrl: string, parentUrl: string): any;
3
+ static getResourcePath(url: string | undefined): any;
4
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const { URI } = require("../../dist/bundle-odata");
4
+ class UrlUtil {
5
+ static join(relativeUrl, parentUrl) {
6
+ // Remove trailing slash, otherwise url join can be incorrect Remove
7
+ // trailing slash, otherwise url join can be incorrect Annotation URLs
8
+ // defined in manifest might end with .../$value/ or .../$value and both
9
+ // are accepted by Gateway and produce the same content with same
10
+ // relative URLs. The first case is actually incorrect and we have to
11
+ // sanitize the same way as UI5. We also trim urls domain/host, because
12
+ // we need to substitute it with destination to download later.
13
+ return new URI(relativeUrl).absoluteTo(parentUrl.replace(/\/$/, "")).toString();
14
+ }
15
+ static getResourcePath(url) {
16
+ // Trim urls domain/host.
17
+ return url && URI.parse(url).path;
18
+ }
19
+ }
20
+ exports.default = UrlUtil;
21
+ //# sourceMappingURL=urlUtil.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ui5/task-adaptation",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Custom task for ui5-builder which allows building UI5 Flexibility Adaptation Projects for SAP BTP, Cloud Foundry environment",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -45,9 +45,10 @@
45
45
  "builtin-modules": "^3.2.0",
46
46
  "crc": "^4.3.2",
47
47
  "dotenv": "^16.0.3",
48
+ "glob": "^10.3.10",
48
49
  "js-yaml": "^4.1.0",
49
50
  "jsdom": "^23.0.1",
50
- "rimraf": "^3.0.2",
51
+ "rimraf": "^5.0.5",
51
52
  "rollup": "^2.56.3",
52
53
  "semver": "^7.3.5",
53
54
  "temp-dir": "^2.0.0",
@@ -62,12 +63,13 @@
62
63
  "@types/jsdom": "^21.1.6",
63
64
  "@types/lodash": "^4.14.196",
64
65
  "@types/mocha": "^9.1.0",
65
- "@types/rimraf": "^3.0.2",
66
+ "@types/rimraf": "^4.0.5",
66
67
  "@types/semver": "^7.3.8",
67
68
  "@types/sinon": "^10.0.16",
68
69
  "chai": "^4.3.4",
69
70
  "chai-as-promised": "^7.1.1",
70
71
  "chalk": "^4.1.2",
72
+ "minimatch": "^9.0.3",
71
73
  "mocha": "^9.2.0",
72
74
  "mock-require": "^3.0.3",
73
75
  "nyc": "^15.1.0",