@ui5/task-adaptation 1.5.2 → 1.6.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.
- package/.hyperspace/pull_request_bot.json +19 -0
- package/CHANGELOG.md +11 -6
- package/dist/appVariantManager.js +1 -1
- package/dist/baseAppManager.js +3 -3
- package/dist/buildStrategy.d.ts +0 -3
- package/dist/buildStrategy.js +0 -7
- package/dist/bundle.d.ts +4 -3
- package/dist/bundle.js +3650 -3595
- package/dist/cache/cacheHolder.d.ts +1 -1
- package/dist/cache/cacheHolder.js +8 -6
- package/dist/index.js +5 -1
- package/dist/model/authenticationError.d.ts +3 -0
- package/dist/model/authenticationError.js +8 -0
- package/dist/model/types.d.ts +12 -0
- package/dist/previewManager.d.ts +13 -0
- package/dist/previewManager.js +142 -0
- package/dist/processors/abapProcessor.d.ts +1 -0
- package/dist/processors/abapProcessor.js +3 -0
- package/dist/processors/cfProcessor.d.ts +2 -1
- package/dist/processors/cfProcessor.js +11 -1
- package/dist/processors/processor.d.ts +2 -1
- package/dist/repositories/html5RepoManager.d.ts +3 -2
- package/dist/repositories/html5RepoManager.js +18 -5
- package/dist/util/cfUtil.d.ts +8 -1
- package/dist/util/cfUtil.js +16 -8
- package/dist/util/movingHandler/changeFileMoveHandler.d.ts +8 -0
- package/dist/util/movingHandler/changeFileMoveHandler.js +77 -0
- package/dist/util/resourceUtil.d.ts +11 -3
- package/dist/util/resourceUtil.js +83 -18
- package/eslint.config.js +4 -3
- package/package.json +19 -20
- package/rollup/amdToEsm.ts +22 -0
- package/rollup/bundle.d.ts +25 -0
- package/rollup/bundleDefinition.js +19 -0
- package/rollup/bundler.ts +35 -0
- package/rollup/overrides/sap/base/config.js +59 -0
- package/rollup/overrides/sap/ui/fl/apply/_internal/flexObjects/AppDescriptorChange.js +68 -0
- package/rollup/overrides/sap/ui/performance/Measurement.js +4 -0
- package/rollup/project/package.json +4 -0
- package/rollup/project/ui5.yaml +13 -0
- package/rollup/project/webapp/manifest.json +5 -0
- package/rollup/rollup.ts +133 -0
- package/rollup/ui5Resolve.ts +145 -0
- package/scripts/publish.ts +256 -0
- package/scripts/test-integration-prep.sh +4 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default class CacheHolder {
|
|
2
2
|
private static TEMP_TASK_DIR;
|
|
3
3
|
private static getTempDir;
|
|
4
|
-
static read(repoName: string, token: string): Map<string, string
|
|
4
|
+
static read(repoName: string, token: string): Promise<Map<string, string>>;
|
|
5
5
|
static write(repoName: string, token: string, files: Map<string, string>): Promise<void>;
|
|
6
6
|
private static isValid;
|
|
7
7
|
/**
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as fsPromises from "fs/promises";
|
|
3
3
|
import * as path from "path";
|
|
4
|
+
import * as os from "node:os";
|
|
4
5
|
import ResourceUtil from "../util/resourceUtil.js";
|
|
5
6
|
import encodeFilename from "filenamify";
|
|
6
7
|
import { getLogger } from "@ui5/logger";
|
|
7
|
-
import tempDir from "temp-dir";
|
|
8
8
|
const log = getLogger("@ui5/task-adaptation::CacheHolder");
|
|
9
9
|
export default class CacheHolder {
|
|
10
10
|
static TEMP_TASK_DIR = "ui5-task-adaptation";
|
|
11
11
|
static getTempDir(...paths) {
|
|
12
|
-
return path.join(
|
|
12
|
+
return path.join(os.tmpdir(), this.TEMP_TASK_DIR, ...paths.map(part => encodeFilename(part, { replacement: "_" })));
|
|
13
13
|
}
|
|
14
14
|
static read(repoName, token) {
|
|
15
15
|
const directory = this.getTempDir(repoName, token);
|
|
16
16
|
if (this.isValid(repoName, "repoName") && this.isValid(token, "token") && fs.existsSync(directory)) {
|
|
17
|
-
return ResourceUtil.
|
|
17
|
+
return ResourceUtil.byGlob(directory, "**/*");
|
|
18
18
|
}
|
|
19
|
+
return Promise.resolve(new Map());
|
|
19
20
|
}
|
|
20
21
|
static async write(repoName, token, files) {
|
|
21
22
|
this.delete(repoName);
|
|
@@ -40,7 +41,7 @@ export default class CacheHolder {
|
|
|
40
41
|
* Clears all cached files
|
|
41
42
|
*/
|
|
42
43
|
static clear() {
|
|
43
|
-
this.deleteDir(path.join(
|
|
44
|
+
this.deleteDir(path.join(os.tmpdir(), this.TEMP_TASK_DIR));
|
|
44
45
|
}
|
|
45
46
|
static deleteDir(directory) {
|
|
46
47
|
if (fs.existsSync(directory)) {
|
|
@@ -67,9 +68,10 @@ export function cached() {
|
|
|
67
68
|
return function (_target, _propertyKey, descriptor) {
|
|
68
69
|
const originalValue = descriptor.value;
|
|
69
70
|
descriptor.value = async function (...args) {
|
|
70
|
-
let files = CacheHolder.read(args[0], args[1]);
|
|
71
|
+
let files = await CacheHolder.read(args[0], args[1]);
|
|
71
72
|
CacheHolder.clearOutdatedExcept(args[0]);
|
|
72
|
-
if (files
|
|
73
|
+
if (files.size === 0) {
|
|
74
|
+
log.verbose(`Cache repo '${args[0]}' with token '${args[1]}' does not contain files. Fetching...`);
|
|
73
75
|
files = await originalValue.apply(this, args);
|
|
74
76
|
await CacheHolder.write(args[0], args[1], files);
|
|
75
77
|
}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import I18nMerger from "./util/i18nMerger.js";
|
|
|
6
6
|
import ResourceUtil from "./util/resourceUtil.js";
|
|
7
7
|
import { determineProcessor } from "./processors/processor.js";
|
|
8
8
|
import FilesUtil from "./util/filesUtil.js";
|
|
9
|
+
import PreviewManager from "./previewManager.js";
|
|
9
10
|
/**
|
|
10
11
|
* Creates an appVariant bundle from the provided resources.
|
|
11
12
|
*/
|
|
@@ -15,6 +16,7 @@ export default ({ workspace, options, taskUtil }) => {
|
|
|
15
16
|
logBuilderVersion();
|
|
16
17
|
const processor = determineProcessor(options.configuration);
|
|
17
18
|
const adaptationProject = await AppVariant.fromWorkspace(workspace, options.projectNamespace);
|
|
19
|
+
const previewManagerPromise = PreviewManager.createFromRoot(adaptationProject.reference, processor);
|
|
18
20
|
const appVariantIdHierarchy = await processor.getAppVariantIdHierarchy(adaptationProject.reference);
|
|
19
21
|
if (appVariantIdHierarchy.length === 0) {
|
|
20
22
|
throw new Error(`No app variant found for reference ${adaptationProject.reference}`);
|
|
@@ -43,7 +45,9 @@ export default ({ workspace, options, taskUtil }) => {
|
|
|
43
45
|
files = FilesUtil.filter(files);
|
|
44
46
|
files = FilesUtil.rename(files, references);
|
|
45
47
|
adaptationProject.omitDeletedResources(files, options.projectNamespace, taskUtil);
|
|
46
|
-
|
|
48
|
+
// Read libs for preview
|
|
49
|
+
const previewManager = await previewManagerPromise;
|
|
50
|
+
const writePromises = new Array(previewManager.processPreviewResources(files));
|
|
47
51
|
files.forEach((content, filename) => {
|
|
48
52
|
const resource = ResourceUtil.createResource(filename, options.projectNamespace, content);
|
|
49
53
|
writePromises.push(workspace.write(resource));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export default class AuthenticationError extends Error {
|
|
2
|
+
constructor(details) {
|
|
3
|
+
super(details);
|
|
4
|
+
this.name = "AuthenticationError";
|
|
5
|
+
this.message = "Authentication error. Use 'cf login' to authenticate in Cloud Foundry" + (details ? `: ${details}` : ".");
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=authenticationError.js.map
|
package/dist/model/types.d.ts
CHANGED
|
@@ -85,6 +85,18 @@ export interface IHTML5RepoInfo {
|
|
|
85
85
|
token: string;
|
|
86
86
|
baseUri: string;
|
|
87
87
|
}
|
|
88
|
+
export interface IReuseLibInfo {
|
|
89
|
+
name: string;
|
|
90
|
+
lazy: boolean;
|
|
91
|
+
html5AppHostId: string;
|
|
92
|
+
html5AppName: string;
|
|
93
|
+
html5AppVersion: string;
|
|
94
|
+
html5CacheBusterToken: string;
|
|
95
|
+
url: {
|
|
96
|
+
uri: string;
|
|
97
|
+
final: boolean;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
88
100
|
export interface IAuth {
|
|
89
101
|
username: string;
|
|
90
102
|
password: string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import IProcessor from "./processors/processor.js";
|
|
2
|
+
export default class PreviewManager {
|
|
3
|
+
private readonly fetchLibsPromises;
|
|
4
|
+
static createFromRoot(appId: string, processor: IProcessor): Promise<PreviewManager>;
|
|
5
|
+
static isPreviewRequested(): boolean;
|
|
6
|
+
private constructor();
|
|
7
|
+
processPreviewResources(baseAppFiles: ReadonlyMap<string, string>): Promise<void>;
|
|
8
|
+
private preReadLibs;
|
|
9
|
+
private searchBaseAppXsAppJsonFile;
|
|
10
|
+
private mergeXsAppJsonFiles;
|
|
11
|
+
private static moveLibraryFiles;
|
|
12
|
+
private static modifyRoutes;
|
|
13
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { getLogger } from "@ui5/logger";
|
|
2
|
+
import ResourceUtil from "./util/resourceUtil.js";
|
|
3
|
+
import path from "path";
|
|
4
|
+
const log = getLogger("@ui5/task-adaptation::PreviewManager");
|
|
5
|
+
const REUSE_DIR = ".adp/reuse";
|
|
6
|
+
const APP_INFO_FILE = "ui5AppInfo.json";
|
|
7
|
+
const XS_APP_JSON_FILE = "xs-app.json";
|
|
8
|
+
export default class PreviewManager {
|
|
9
|
+
fetchLibsPromises = new Map();
|
|
10
|
+
static async createFromRoot(appId, processor) {
|
|
11
|
+
let ui5AppInfo = "";
|
|
12
|
+
if (PreviewManager.isPreviewRequested()) {
|
|
13
|
+
try {
|
|
14
|
+
ui5AppInfo = await ResourceUtil.readInProject(APP_INFO_FILE);
|
|
15
|
+
}
|
|
16
|
+
catch (_err) {
|
|
17
|
+
log.verbose("Preview mode not requested (env variable ADP_BUILDER_MODE=preview is not set), skipping preview resources processing.");
|
|
18
|
+
throw new Error(`ui5AppInfo.json is missing in project root, cannot process preview resources.`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return new PreviewManager(appId, ui5AppInfo, processor);
|
|
22
|
+
}
|
|
23
|
+
static isPreviewRequested() {
|
|
24
|
+
return process.env.ADP_BUILDER_MODE === "preview";
|
|
25
|
+
}
|
|
26
|
+
constructor(appId, ui5AppInfo, processor) {
|
|
27
|
+
// If no ui5AppInfo is provided, no preview processing is needed
|
|
28
|
+
if (!ui5AppInfo) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const appInfo = JSON.parse(ui5AppInfo)[appId];
|
|
32
|
+
if (!appInfo) {
|
|
33
|
+
throw new Error(`No app info found for original app id '${appId}' in ui5AppInfo.json`);
|
|
34
|
+
}
|
|
35
|
+
const reuseLibs = appInfo.asyncHints.libs.filter(lib => lib.html5AppHostId && lib.html5AppName && lib.html5AppVersion);
|
|
36
|
+
if (reuseLibs.length > 0) {
|
|
37
|
+
this.fetchLibsPromises = this.preReadLibs(reuseLibs, processor);
|
|
38
|
+
}
|
|
39
|
+
const logMap = {
|
|
40
|
+
Error: log.error,
|
|
41
|
+
Warning: log.warning,
|
|
42
|
+
Info: log.info,
|
|
43
|
+
debug: log.verbose
|
|
44
|
+
};
|
|
45
|
+
appInfo.messages.forEach((message) => {
|
|
46
|
+
const logger = logMap[message.severity] || log.info;
|
|
47
|
+
logger(`ui5AppInfo.json: ${message.severity} Message: ${message.message} (Error Code: ${message.errorCode})`);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async processPreviewResources(baseAppFiles) {
|
|
51
|
+
log.verbose(`Downloading reuse libraries to reuse folder`);
|
|
52
|
+
const xsAppFiles = new Map();
|
|
53
|
+
if (this.fetchLibsPromises.size === 0) {
|
|
54
|
+
log.verbose("No reuse libraries defined in ui5AppInfo.json for preview");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const mergedFiles = new Map();
|
|
58
|
+
for (const [libName, libFilesPromise] of this.fetchLibsPromises) {
|
|
59
|
+
const libFiles = await libFilesPromise;
|
|
60
|
+
if (!libFiles || libFiles.size === 0) {
|
|
61
|
+
log.warn(`No files found in reuse library ${libName} for preview`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
for (const [filename, content] of libFiles) {
|
|
65
|
+
mergedFiles.set(filename, content);
|
|
66
|
+
if (filename.includes(XS_APP_JSON_FILE)) {
|
|
67
|
+
xsAppFiles.set(filename, content);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
this.searchBaseAppXsAppJsonFile(xsAppFiles, baseAppFiles);
|
|
72
|
+
this.mergeXsAppJsonFiles(xsAppFiles, mergedFiles);
|
|
73
|
+
await ResourceUtil.writeInProject(REUSE_DIR, mergedFiles);
|
|
74
|
+
}
|
|
75
|
+
preReadLibs(reuseLibs, processor) {
|
|
76
|
+
const promises = new Map();
|
|
77
|
+
reuseLibs.forEach(lib => {
|
|
78
|
+
log.info(`Downloading reuse library '${lib.html5AppName}' version '${lib.html5AppVersion}'`);
|
|
79
|
+
const promise = processor
|
|
80
|
+
.fetchReuseLib(lib.html5AppName, lib.html5CacheBusterToken, lib)
|
|
81
|
+
.then(libFiles => PreviewManager.moveLibraryFiles(libFiles, lib.html5AppName, lib.name));
|
|
82
|
+
promises.set(lib.html5AppName, promise);
|
|
83
|
+
});
|
|
84
|
+
return promises;
|
|
85
|
+
}
|
|
86
|
+
searchBaseAppXsAppJsonFile(xsAppFiles, baseAppFiles) {
|
|
87
|
+
const xsAppJsonContent = baseAppFiles.get(XS_APP_JSON_FILE);
|
|
88
|
+
if (xsAppJsonContent) {
|
|
89
|
+
xsAppFiles.set(XS_APP_JSON_FILE, xsAppJsonContent);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
log.warn("xs-app.json is missing in the downloaded base app files for preview");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
mergeXsAppJsonFiles(xsAppFiles, files) {
|
|
96
|
+
// Start with empty xs-app.json
|
|
97
|
+
// welcomeFile is not needed for preview
|
|
98
|
+
const mergedXsAppJson = {
|
|
99
|
+
authenticationMethod: "none",
|
|
100
|
+
routes: []
|
|
101
|
+
};
|
|
102
|
+
xsAppFiles.forEach((xsAppInfoContent) => {
|
|
103
|
+
const xsAppInfo = JSON.parse(xsAppInfoContent);
|
|
104
|
+
if (mergedXsAppJson.authenticationMethod === "none" && xsAppInfo.authenticationMethod) {
|
|
105
|
+
mergedXsAppJson.authenticationMethod = xsAppInfo.authenticationMethod;
|
|
106
|
+
}
|
|
107
|
+
if (Array.isArray(xsAppInfo.routes)) {
|
|
108
|
+
mergedXsAppJson.routes = mergedXsAppJson.routes.concat(xsAppInfo.routes);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
files.set(XS_APP_JSON_FILE, JSON.stringify(mergedXsAppJson, null, 2));
|
|
112
|
+
}
|
|
113
|
+
static moveLibraryFiles(inputFiles, libraryName, libId) {
|
|
114
|
+
const files = new Map();
|
|
115
|
+
inputFiles.forEach((content, filename) => {
|
|
116
|
+
const newFilename = path.join(libraryName, filename);
|
|
117
|
+
// Source path in xs-app.json needs to be adjusted to new location
|
|
118
|
+
if (filename.includes(XS_APP_JSON_FILE)) {
|
|
119
|
+
content = PreviewManager.modifyRoutes(content, libraryName, libId);
|
|
120
|
+
}
|
|
121
|
+
files.set(newFilename, content);
|
|
122
|
+
});
|
|
123
|
+
return files;
|
|
124
|
+
}
|
|
125
|
+
;
|
|
126
|
+
static modifyRoutes(xsAppJson, libName, libId) {
|
|
127
|
+
const xsApp = JSON.parse(xsAppJson);
|
|
128
|
+
xsApp.routes = xsApp.routes.map((route) => {
|
|
129
|
+
route.source = route.source.replace(new RegExp("^\\^\\/?(resources/)?"), `^/resources/${libId.replaceAll(".", "/")}/`);
|
|
130
|
+
if (route.service === "html5-apps-repo-rt") {
|
|
131
|
+
route = {
|
|
132
|
+
source: route.source,
|
|
133
|
+
target: route.target,
|
|
134
|
+
localDir: path.join(REUSE_DIR, libName)
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return route;
|
|
138
|
+
});
|
|
139
|
+
return JSON.stringify(xsApp);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=previewManager.js.map
|
|
@@ -10,6 +10,7 @@ export default class AbapProcessor implements IProcessor {
|
|
|
10
10
|
constructor(configuration: IConfiguration, abapRepoManager: AbapRepoManager, annotationManager: AnnotationManager);
|
|
11
11
|
getAppVariantIdHierarchy(appId: string): Promise<IAppVariantIdHierarchyItem[]>;
|
|
12
12
|
fetch(repoName: string, _cachebusterToken: string): Promise<Map<string, string>>;
|
|
13
|
+
fetchReuseLib(): Promise<Map<string, string>>;
|
|
13
14
|
validateConfiguration(): void;
|
|
14
15
|
updateLandscapeSpecificContent(baseAppManifest: any, baseAppFiles: Map<string, string>, appVariantId: string, prefix: string): Promise<void>;
|
|
15
16
|
getConfigurationType(): string;
|
|
@@ -22,6 +22,9 @@ export default class AbapProcessor {
|
|
|
22
22
|
fetch(repoName, _cachebusterToken) {
|
|
23
23
|
return this.abapRepoManager.fetch(repoName);
|
|
24
24
|
}
|
|
25
|
+
fetchReuseLib() {
|
|
26
|
+
throw new Error("Preview is not available on SAP S/4HANA On-Premise or Cloud Systems. Please create a ticket on CA-UI5-FL-ADP-BAS component.");
|
|
27
|
+
}
|
|
25
28
|
validateConfiguration() {
|
|
26
29
|
// validate general app config
|
|
27
30
|
const properties = ["appName"];
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import IAppInfo from "../model/appVariantIdHierarchyItem.js";
|
|
2
|
-
import { IConfiguration } from "../model/types.js";
|
|
2
|
+
import { IConfiguration, IReuseLibInfo } from "../model/types.js";
|
|
3
3
|
import IProcessor from "./processor.js";
|
|
4
4
|
export default class CFProcessor implements IProcessor {
|
|
5
5
|
private configuration;
|
|
6
6
|
constructor(configuration: IConfiguration);
|
|
7
7
|
getAppVariantIdHierarchy(appId: string): Promise<IAppInfo[]>;
|
|
8
8
|
fetch(_repoName: string, _cachebusterToken: string): Promise<Map<string, string>>;
|
|
9
|
+
fetchReuseLib(_libName: string, _cachebusterToken: string, lib: IReuseLibInfo): Promise<Map<string, string>>;
|
|
9
10
|
validateConfiguration(): void;
|
|
10
11
|
updateLandscapeSpecificContent(baseAppManifest: any, baseAppFiles: Map<string, string>): Promise<void>;
|
|
11
12
|
private updateXsAppJson;
|
|
@@ -9,6 +9,7 @@ import { cached } from "../cache/cacheHolder.js";
|
|
|
9
9
|
import { validateObject } from "../util/commonUtil.js";
|
|
10
10
|
import CFUtil from "../util/cfUtil.js";
|
|
11
11
|
import { getLogger } from "@ui5/logger";
|
|
12
|
+
import PreviewManager from "../previewManager.js";
|
|
12
13
|
const log = getLogger("@ui5/task-adaptation::CFProcessor");
|
|
13
14
|
export default class CFProcessor {
|
|
14
15
|
configuration;
|
|
@@ -26,12 +27,18 @@ export default class CFProcessor {
|
|
|
26
27
|
fetch(_repoName, _cachebusterToken) {
|
|
27
28
|
return HTML5RepoManager.getBaseAppFiles(this.configuration);
|
|
28
29
|
}
|
|
30
|
+
fetchReuseLib(_libName, _cachebusterToken, lib) {
|
|
31
|
+
return HTML5RepoManager.getReuseLibFiles(this.configuration, lib);
|
|
32
|
+
}
|
|
29
33
|
validateConfiguration() {
|
|
30
34
|
validateObject(this.configuration, ["appHostId", "appName", "appVersion"], "should be specified in ui5.yaml configuration");
|
|
31
35
|
}
|
|
32
36
|
async updateLandscapeSpecificContent(baseAppManifest, baseAppFiles) {
|
|
33
37
|
this.updateCloudPlatform(baseAppManifest);
|
|
34
|
-
|
|
38
|
+
// Preview uses destinations and does not require xs-app.json updates
|
|
39
|
+
if (!PreviewManager.isPreviewRequested()) {
|
|
40
|
+
await this.updateXsAppJson(baseAppFiles);
|
|
41
|
+
}
|
|
35
42
|
}
|
|
36
43
|
async updateXsAppJson(baseAppFiles) {
|
|
37
44
|
const xsAppJsonContent = baseAppFiles.get("xs-app.json");
|
|
@@ -119,4 +126,7 @@ export default class CFProcessor {
|
|
|
119
126
|
__decorate([
|
|
120
127
|
cached()
|
|
121
128
|
], CFProcessor.prototype, "fetch", null);
|
|
129
|
+
__decorate([
|
|
130
|
+
cached()
|
|
131
|
+
], CFProcessor.prototype, "fetchReuseLib", null);
|
|
122
132
|
//# sourceMappingURL=cfProcessor.js.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import IAppVariantIdHierarchyItem from "../model/appVariantIdHierarchyItem.js";
|
|
2
|
-
import { IConfiguration } from "../model/types.js";
|
|
2
|
+
import { IConfiguration, IReuseLibInfo } from "../model/types.js";
|
|
3
3
|
export default interface IProcessor {
|
|
4
4
|
getAppVariantIdHierarchy(appId: string): Promise<IAppVariantIdHierarchyItem[]>;
|
|
5
5
|
fetch(repoName: string, cachebusterToken: string): Promise<Map<string, string>>;
|
|
6
|
+
fetchReuseLib(repoName: string, cachebusterToken: string, lib: IReuseLibInfo): Promise<Map<string, string>>;
|
|
6
7
|
createAppVariantHierarchyItem(appVariantId: string, version: string): void;
|
|
7
8
|
updateLandscapeSpecificContent(baseAppManifest: any, baseAppFiles: Map<string, string>, appVariantId: string, prefix: string): Promise<void>;
|
|
8
9
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { IConfiguration } from "./../model/types.js";
|
|
1
|
+
import { IConfiguration, IReuseLibInfo } from "./../model/types.js";
|
|
2
2
|
export default class HTML5RepoManager {
|
|
3
3
|
static getBaseAppFiles(configuration: IConfiguration): Promise<Map<string, string>>;
|
|
4
4
|
static getMetadata(configuration: IConfiguration): Promise<any>;
|
|
5
|
+
static getReuseLibFiles(configuration: IConfiguration, lib: IReuseLibInfo): Promise<Map<string, string>>;
|
|
5
6
|
private static getHtml5RepoInfo;
|
|
6
7
|
private static getHTML5Credentials;
|
|
7
8
|
private static getToken;
|
|
8
9
|
private static requestMetadata;
|
|
9
|
-
private static
|
|
10
|
+
private static getAppZipEntries;
|
|
10
11
|
private static download;
|
|
11
12
|
}
|
|
@@ -6,12 +6,26 @@ const log = getLogger("@ui5/task-adaptation::HTML5RepoManager");
|
|
|
6
6
|
export default class HTML5RepoManager {
|
|
7
7
|
static async getBaseAppFiles(configuration) {
|
|
8
8
|
const { token, baseUri } = await this.getHtml5RepoInfo(configuration);
|
|
9
|
-
|
|
9
|
+
const app = {
|
|
10
|
+
appName: configuration.appName,
|
|
11
|
+
appVersion: configuration.appVersion,
|
|
12
|
+
appHostId: configuration.appHostId
|
|
13
|
+
};
|
|
14
|
+
return this.getAppZipEntries(app, baseUri, token);
|
|
10
15
|
}
|
|
11
16
|
static async getMetadata(configuration) {
|
|
12
17
|
const { token, baseUri } = await this.getHtml5RepoInfo(configuration);
|
|
13
18
|
return this.requestMetadata(configuration, baseUri, token);
|
|
14
19
|
}
|
|
20
|
+
static async getReuseLibFiles(configuration, lib) {
|
|
21
|
+
const { token, baseUri } = await this.getHtml5RepoInfo(configuration);
|
|
22
|
+
const libAppData = {
|
|
23
|
+
appName: lib.html5AppName,
|
|
24
|
+
appVersion: lib.html5AppVersion,
|
|
25
|
+
appHostId: lib.html5AppHostId
|
|
26
|
+
};
|
|
27
|
+
return this.getAppZipEntries(libAppData, baseUri, token);
|
|
28
|
+
}
|
|
15
29
|
static async getHtml5RepoInfo(configuration) {
|
|
16
30
|
const spaceGuid = await CFUtil.getSpaceGuid(configuration?.space);
|
|
17
31
|
const credentials = await this.getHTML5Credentials(spaceGuid);
|
|
@@ -65,10 +79,9 @@ export default class HTML5RepoManager {
|
|
|
65
79
|
const metadata = await RequestUtil.get(uri, requestOptions);
|
|
66
80
|
return metadata.find((item) => item.appHostId === appHostId && item.applicationName === appName && item.applicationVersion === appVersion);
|
|
67
81
|
}
|
|
68
|
-
static async
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const zip = await this.download(token, appHostId, uri);
|
|
82
|
+
static async getAppZipEntries(app, html5RepoBaseUri, token) {
|
|
83
|
+
const uri = `${html5RepoBaseUri}/applications/content/${app.appName}-${app.appVersion}/`;
|
|
84
|
+
const zip = await this.download(token, app.appHostId, uri);
|
|
72
85
|
return unzipZipEntries(zip);
|
|
73
86
|
}
|
|
74
87
|
static async download(token, appHostId, uri) {
|
package/dist/util/cfUtil.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export default class CFUtil {
|
|
|
15
15
|
private static createServiceKey;
|
|
16
16
|
private static deleteServiceKeyUnsafe;
|
|
17
17
|
private static getServiceInstance;
|
|
18
|
-
static
|
|
18
|
+
static processCfErrors(errors?: ICfError[]): void;
|
|
19
19
|
static requestCfApi(url: string): Promise<IResource[]>;
|
|
20
20
|
static getOAuthToken(): Promise<string>;
|
|
21
21
|
private static cfExecute;
|
|
@@ -76,3 +76,10 @@ export default class CFUtil {
|
|
|
76
76
|
*/
|
|
77
77
|
static getSpaceGuid(spaceGuid?: string): Promise<string>;
|
|
78
78
|
}
|
|
79
|
+
interface ICfError {
|
|
80
|
+
code: number;
|
|
81
|
+
title: string;
|
|
82
|
+
detail: string;
|
|
83
|
+
[key: string]: any;
|
|
84
|
+
}
|
|
85
|
+
export {};
|
package/dist/util/cfUtil.js
CHANGED
|
@@ -3,6 +3,7 @@ import { getSpaceGuidThrowIfUndefined } from "@sap/cf-tools/out/src/utils.js";
|
|
|
3
3
|
import { Cli } from "@sap/cf-tools/out/src/cli.js";
|
|
4
4
|
import { eFilters } from "@sap/cf-tools/out/src/types.js";
|
|
5
5
|
import { getLogger } from "@ui5/logger";
|
|
6
|
+
import AuthenticationError from "../model/authenticationError.js";
|
|
6
7
|
const log = getLogger("@ui5/task-adaptation::CFUtil");
|
|
7
8
|
export default class CFUtil {
|
|
8
9
|
/**
|
|
@@ -103,19 +104,20 @@ export default class CFUtil {
|
|
|
103
104
|
guid: service.guid
|
|
104
105
|
}));
|
|
105
106
|
}
|
|
106
|
-
static
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
throw new
|
|
107
|
+
static processCfErrors(errors) {
|
|
108
|
+
if (!errors || errors.length === 0) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const authError = errors.find(e => e.title === "CF-NotAuthenticated" || e.code === 10002);
|
|
112
|
+
if (authError) {
|
|
113
|
+
throw new AuthenticationError(authError.detail);
|
|
113
114
|
}
|
|
115
|
+
throw new Error(`Failed sending request to Cloud Foundry: ${JSON.stringify(errors)}`);
|
|
114
116
|
}
|
|
115
117
|
static async requestCfApi(url) {
|
|
116
118
|
const response = await this.cfExecute(["curl", url]);
|
|
117
119
|
const json = this.parseJson(response);
|
|
118
|
-
this.
|
|
120
|
+
this.processCfErrors(json?.errors);
|
|
119
121
|
const resources = json?.resources;
|
|
120
122
|
const totalPages = json?.pagination?.total_pages;
|
|
121
123
|
if (totalPages > 1) {
|
|
@@ -142,11 +144,17 @@ export default class CFUtil {
|
|
|
142
144
|
if (errorValues?.length > 0) {
|
|
143
145
|
log.verbose(this.errorsToString(errorValues));
|
|
144
146
|
}
|
|
147
|
+
if (response.stdout === "\n") {
|
|
148
|
+
throw new AuthenticationError();
|
|
149
|
+
}
|
|
145
150
|
return response.stdout;
|
|
146
151
|
}
|
|
147
152
|
errors.add(response.error || response.stderr);
|
|
148
153
|
}
|
|
149
154
|
catch (error) {
|
|
155
|
+
if (error instanceof AuthenticationError) {
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
150
158
|
errors.add(error.message);
|
|
151
159
|
}
|
|
152
160
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const moveFiles: (inputFiles: ReadonlyMap<string, string>, prefix: string, id: string) => {
|
|
2
|
+
files: Map<string, string>;
|
|
3
|
+
renamingPaths: Map<string, string>;
|
|
4
|
+
};
|
|
5
|
+
export declare const moveFile: (filename: string, content: string, prefix: string, id: string) => {
|
|
6
|
+
newFilename: string;
|
|
7
|
+
renamingPath: Map<string, string>;
|
|
8
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { isManifestChange } from "../commonUtil.js";
|
|
3
|
+
const EXT_DIR = "ext/";
|
|
4
|
+
const CHANGES_DIR = "changes/";
|
|
5
|
+
const nameSpaceRegex = new RegExp(`(?<=ControllerExtension.extend\\(")([^"]*)(?=")`);
|
|
6
|
+
/**
|
|
7
|
+
* 2p. We move to appVariantFolder (prefix) only files that go along the
|
|
8
|
+
* change files, like js or fragments. Changes are renamed in resources,
|
|
9
|
+
* packed in flexibility-bundle and removed.
|
|
10
|
+
* @param filename - The filename relative to the root
|
|
11
|
+
*/
|
|
12
|
+
function shouldMove(filename, content) {
|
|
13
|
+
//TODO: is it more reliable to check change/fileType?
|
|
14
|
+
if (isManifestChange(filename, content)) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
return filename.startsWith(CHANGES_DIR) && [".change", ".variant", ".ctrl_variant", ".ctrl_variant_change", ".ctrl_variant_management_change"].every(ext => !filename.endsWith(ext))
|
|
18
|
+
|| filename.startsWith(EXT_DIR);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* For controller extension the namespace needs a prefix
|
|
22
|
+
* The namespace needs to be unique for clear identification
|
|
23
|
+
* If controller extension have the same namespace the last one will be used
|
|
24
|
+
* @param filename - The filename relative to the root
|
|
25
|
+
* @returns
|
|
26
|
+
*/
|
|
27
|
+
function shouldNamespaceRenamed(filename) {
|
|
28
|
+
return filename.startsWith(CHANGES_DIR) && filename.endsWith(".js");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns path without first directory (which is usually "changes" or
|
|
32
|
+
* "ext") and without file extension. For example, for
|
|
33
|
+
* "changes/coding/FixingDay.js" it returns "coding/FixingDay", for
|
|
34
|
+
* "changes/customer_app_variant5/fragments/hello_world_fixing_day.fragment.xml"
|
|
35
|
+
* it returns
|
|
36
|
+
* "changes/customer_app_variant5/fragments/hello_world_fixing_day". We
|
|
37
|
+
* remove all extensions not to rename it with slashes instead of dots. And
|
|
38
|
+
* we also exclude "changes" or "ext" include the prefix after them and then
|
|
39
|
+
* the filename. Without 'changes' or 'ext' directory, we need to replace
|
|
40
|
+
* only rest: coding/FixingDay.js to app_var_id1/coding/FixingDay.js
|
|
41
|
+
* @param filename
|
|
42
|
+
* @returns
|
|
43
|
+
*/
|
|
44
|
+
function getPathWithoutExtensions(filename) {
|
|
45
|
+
const [_dir, ...rest] = filename.split("/");
|
|
46
|
+
const restWOExt = [...rest];
|
|
47
|
+
restWOExt[restWOExt.length - 1] = restWOExt[restWOExt.length - 1].split(".")[0];
|
|
48
|
+
return restWOExt.join("/");
|
|
49
|
+
}
|
|
50
|
+
export const moveFiles = (inputFiles, prefix, id) => {
|
|
51
|
+
const files = new Map();
|
|
52
|
+
let renamingPaths = new Map();
|
|
53
|
+
inputFiles.forEach((content, filename) => {
|
|
54
|
+
const { newFilename, renamingPath } = moveFile(filename, content, prefix, id);
|
|
55
|
+
files.set(newFilename, content);
|
|
56
|
+
renamingPaths = new Map([...renamingPaths, ...renamingPath]);
|
|
57
|
+
});
|
|
58
|
+
return { files, renamingPaths };
|
|
59
|
+
};
|
|
60
|
+
export const moveFile = (filename, content, prefix, id) => {
|
|
61
|
+
let newFilename = filename;
|
|
62
|
+
let renamingPath = new Map();
|
|
63
|
+
if (shouldMove(filename, content)) {
|
|
64
|
+
const [dir, ...rest] = filename.split("/");
|
|
65
|
+
newFilename = path.join(dir, prefix, rest.join("/"));
|
|
66
|
+
const restWOExtPath = getPathWithoutExtensions(filename);
|
|
67
|
+
renamingPath.set(restWOExtPath, path.join(prefix, restWOExtPath));
|
|
68
|
+
}
|
|
69
|
+
if (shouldNamespaceRenamed(filename)) {
|
|
70
|
+
const namespaceMatch = nameSpaceRegex.exec(content.trim());
|
|
71
|
+
const controllerExtensionPath = namespaceMatch ? namespaceMatch[0] : "";
|
|
72
|
+
const fileName = controllerExtensionPath.replace(id, "");
|
|
73
|
+
renamingPath.set(controllerExtensionPath, `${id}.${prefix}${fileName}`);
|
|
74
|
+
}
|
|
75
|
+
return { newFilename, renamingPath };
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=changeFileMoveHandler.js.map
|
|
@@ -3,11 +3,19 @@ export default class ResourceUtil {
|
|
|
3
3
|
static relativeToRoot(resourcePath: string, projectNamespace?: string): string;
|
|
4
4
|
static getResourcePath(projectNamespace?: string, ...paths: string[]): string;
|
|
5
5
|
static write(dir: string, files: Map<string, string>): Promise<void[]>;
|
|
6
|
-
|
|
7
|
-
static
|
|
6
|
+
static writeInProject(dir: string, files: Map<string, string>): Promise<void[]>;
|
|
7
|
+
static byGlob(dir: string, pattern: string, excludes?: string[]): Promise<Map<string, string>>;
|
|
8
|
+
static byGlobInProject(pattern: string, excludes?: string[]): Promise<Map<string, string>>;
|
|
9
|
+
static readInProject(filepath: string): Promise<string>;
|
|
8
10
|
static getString(resource: any): Promise<string>;
|
|
9
11
|
static getJson(resource: any): Promise<any>;
|
|
10
12
|
static setString(resource: any, str: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Check whether a file or directory exists (non-throwing).
|
|
15
|
+
* @param filePath Absolute or relative path
|
|
16
|
+
* @returns true if the path exists, false if not
|
|
17
|
+
*/
|
|
18
|
+
private static fileExists;
|
|
11
19
|
static createResource(filename: string, projectNamespace: string, content: string): any;
|
|
12
|
-
static toFileMap(resources: ReadonlyArray<Resource>, projectNamespace
|
|
20
|
+
static toFileMap(resources: ReadonlyArray<Resource>, projectNamespace?: string): Promise<Map<string, string>>;
|
|
13
21
|
}
|