@ui5/task-adaptation 1.3.1 → 1.3.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 +5 -1
- package/dist/annotationManager.d.ts +18 -0
- package/dist/annotationManager.js +79 -0
- package/dist/annotations/comparator/comparator.d.ts +47 -0
- package/dist/annotations/comparator/comparator.js +283 -0
- package/dist/annotations/comparator/diffCase.d.ts +4 -0
- package/dist/annotations/comparator/diffCase.js +2 -0
- package/dist/annotations/comparator/interchangableCase.d.ts +25 -0
- package/dist/annotations/comparator/interchangableCase.js +60 -0
- package/dist/annotations/converter/metadataJsonReferenceUtil.d.ts +12 -0
- package/dist/annotations/converter/metadataJsonReferenceUtil.js +48 -0
- package/dist/annotations/converter/metadataJsonUtil.d.ts +30 -0
- package/dist/annotations/converter/metadataJsonUtil.js +70 -0
- package/dist/annotations/converter/ui5JsonConverter.d.ts +21 -0
- package/dist/annotations/converter/ui5JsonConverter.js +252 -0
- package/dist/annotations/converter/ui5MetadataJsonUtil.d.ts +3 -0
- package/dist/annotations/converter/ui5MetadataJsonUtil.js +10 -0
- package/dist/annotations/converter/ui5XmlConverter.d.ts +5 -0
- package/dist/annotations/converter/ui5XmlConverter.js +14 -0
- package/dist/annotations/dataSource/dataSource.d.ts +34 -0
- package/dist/annotations/dataSource/dataSource.js +62 -0
- package/dist/annotations/dataSource/dataSourceManager.d.ts +12 -0
- package/dist/annotations/dataSource/dataSourceManager.js +45 -0
- package/dist/annotations/dataSource/dataSourceOData.d.ts +17 -0
- package/dist/annotations/dataSource/dataSourceOData.js +45 -0
- package/dist/annotations/dataSource/dataSourceODataAnnotation.d.ts +6 -0
- package/dist/annotations/dataSource/dataSourceODataAnnotation.js +16 -0
- package/dist/annotations/serviceRequestor.d.ts +9 -0
- package/dist/annotations/serviceRequestor.js +73 -0
- package/dist/annotations/transformers/convertV2ToV4.d.ts +4 -0
- package/dist/annotations/transformers/convertV2ToV4.js +13 -0
- package/dist/annotations/transformers/makeAnnotationNamespaceUnique.d.ts +6 -0
- package/dist/annotations/transformers/makeAnnotationNamespaceUnique.js +41 -0
- package/dist/annotations/transformers/removeAllSchemaNodesExceptAnnotations.d.ts +4 -0
- package/dist/annotations/transformers/removeAllSchemaNodesExceptAnnotations.js +14 -0
- package/dist/annotations/transformers/transformer.d.ts +12 -0
- package/dist/annotations/transformers/transformer.js +2 -0
- package/dist/annotations/transformers/traverseReferences.d.ts +9 -0
- package/dist/annotations/transformers/traverseReferences.js +66 -0
- package/dist/appVariantManager.d.ts +12 -0
- package/dist/appVariantManager.js +102 -0
- package/dist/baseAppManager.d.ts +29 -0
- package/dist/baseAppManager.js +139 -0
- package/dist/buildStrategy.d.ts +7 -0
- package/dist/buildStrategy.js +19 -0
- package/dist/bundle.d.ts +25 -0
- package/dist/bundle.js +6975 -0
- package/dist/cache/annotationsCacheManager.d.ts +8 -0
- package/dist/cache/annotationsCacheManager.js +16 -0
- package/dist/cache/baseAppFilesCacheManager.d.ts +6 -0
- package/dist/cache/baseAppFilesCacheManager.js +12 -0
- package/dist/cache/cacheManager.d.ts +16 -0
- package/dist/cache/cacheManager.js +65 -0
- package/dist/i18nManager.d.ts +43 -0
- package/dist/i18nManager.js +203 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +25 -0
- package/dist/model/annotationDiffStructureError.d.ts +3 -0
- package/dist/model/annotationDiffStructureError.js +8 -0
- package/dist/model/language.d.ts +13 -0
- package/dist/model/language.js +37 -0
- package/dist/model/noAuthorizationProvidedError.d.ts +3 -0
- package/dist/model/noAuthorizationProvidedError.js +6 -0
- package/dist/model/serverError.d.ts +3 -0
- package/dist/model/serverError.js +6 -0
- package/dist/model/types.d.ts +119 -0
- package/dist/model/types.js +2 -0
- package/dist/processors/abapProcessor.d.ts +21 -0
- package/dist/processors/abapProcessor.js +37 -0
- package/dist/processors/cfProcessor.d.ts +17 -0
- package/dist/processors/cfProcessor.js +45 -0
- package/dist/processors/processor.d.ts +7 -0
- package/dist/processors/processor.js +32 -0
- package/dist/repositories/abapRepoManager.d.ts +13 -0
- package/dist/repositories/abapRepoManager.js +82 -0
- package/dist/repositories/html5RepoManager.d.ts +11 -0
- package/dist/repositories/html5RepoManager.js +87 -0
- package/dist/util/cfUtil.d.ts +30 -0
- package/dist/util/cfUtil.js +171 -0
- package/dist/util/commonUtil.d.ts +13 -0
- package/dist/util/commonUtil.js +118 -0
- package/dist/util/i18nMerger.d.ts +32 -0
- package/dist/util/i18nMerger.js +99 -0
- package/dist/util/requestUtil.d.ts +8 -0
- package/dist/util/requestUtil.js +54 -0
- package/dist/util/resourceUtil.d.ts +11 -0
- package/dist/util/resourceUtil.js +62 -0
- package/dist/util/urlUtil.d.ts +4 -0
- package/dist/util/urlUtil.js +18 -0
- package/dist/util/xmlUtil.d.ts +4 -0
- package/dist/util/xmlUtil.js +21 -0
- package/dist/util/zipUtil.d.ts +2 -0
- package/dist/util/zipUtil.js +16 -0
- package/package.json +2 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { validateObject } from "../util/commonUtil.js";
|
|
2
|
+
import Language from "../model/language.js";
|
|
3
|
+
export default class AbapProcessor {
|
|
4
|
+
abapRepoManager;
|
|
5
|
+
configuration;
|
|
6
|
+
cacheManager;
|
|
7
|
+
annotationManager;
|
|
8
|
+
constructor(configuration, cacheManager, abapRepoManager, annotationManager) {
|
|
9
|
+
this.configuration = configuration;
|
|
10
|
+
this.abapRepoManager = abapRepoManager;
|
|
11
|
+
this.cacheManager = cacheManager;
|
|
12
|
+
this.annotationManager = annotationManager;
|
|
13
|
+
}
|
|
14
|
+
getBaseAppFiles(baseAppId) {
|
|
15
|
+
return this.cacheManager.getFiles(() => this.abapRepoManager.getMetadata(baseAppId), () => this.abapRepoManager.downloadBaseAppFiles());
|
|
16
|
+
}
|
|
17
|
+
validateConfiguration() {
|
|
18
|
+
validateObject(this.configuration, ["destination", "appName"], "should be specified in ui5.yaml configuration");
|
|
19
|
+
}
|
|
20
|
+
async updateLandscapeSpecificContent(renamedBaseAppManifest, baseAppFiles) {
|
|
21
|
+
const files = await this.annotationManager.process(renamedBaseAppManifest, (Language.create(this.configuration.languages)));
|
|
22
|
+
if (baseAppFiles) {
|
|
23
|
+
files.forEach((value, key) => baseAppFiles.set(key, value));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
getConfigurationType() {
|
|
27
|
+
return "abap";
|
|
28
|
+
}
|
|
29
|
+
createAppVariantHierarchyItem(appVariantId, version) {
|
|
30
|
+
return {
|
|
31
|
+
appVariantId,
|
|
32
|
+
version,
|
|
33
|
+
layer: "VENDOR"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=abapProcessor.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import BaseAppFilesCacheManager from "../cache/baseAppFilesCacheManager.js";
|
|
2
|
+
import { IConfiguration } from "../model/types.js";
|
|
3
|
+
import IProcessor from "./processor.js";
|
|
4
|
+
export default class CFProcessor implements IProcessor {
|
|
5
|
+
private configuration;
|
|
6
|
+
private cacheManager;
|
|
7
|
+
constructor(configuration: IConfiguration, cacheManager: BaseAppFilesCacheManager);
|
|
8
|
+
getBaseAppFiles(): Promise<Map<string, string>>;
|
|
9
|
+
validateConfiguration(): void;
|
|
10
|
+
updateLandscapeSpecificContent(renamedBaseAppManifest: any): Promise<void>;
|
|
11
|
+
private updateCloudPlatform;
|
|
12
|
+
getConfigurationType(): string;
|
|
13
|
+
createAppVariantHierarchyItem(appVariantId: string, version: string): {
|
|
14
|
+
appVariantId: string;
|
|
15
|
+
version: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import HTML5RepoManager from "../repositories/html5RepoManager.js";
|
|
2
|
+
import { validateObject } from "../util/commonUtil.js";
|
|
3
|
+
export default class CFProcessor {
|
|
4
|
+
configuration;
|
|
5
|
+
cacheManager;
|
|
6
|
+
constructor(configuration, cacheManager) {
|
|
7
|
+
this.configuration = configuration;
|
|
8
|
+
this.cacheManager = cacheManager;
|
|
9
|
+
}
|
|
10
|
+
async getBaseAppFiles() {
|
|
11
|
+
return this.cacheManager.getFiles(() => HTML5RepoManager.getMetadata(this.configuration), () => HTML5RepoManager.getBaseAppFiles(this.configuration));
|
|
12
|
+
}
|
|
13
|
+
validateConfiguration() {
|
|
14
|
+
validateObject(this.configuration, ["appHostId", "appName", "appVersion"], "should be specified in ui5.yaml configuration");
|
|
15
|
+
}
|
|
16
|
+
async updateLandscapeSpecificContent(renamedBaseAppManifest) {
|
|
17
|
+
this.updateCloudPlatform(renamedBaseAppManifest);
|
|
18
|
+
}
|
|
19
|
+
updateCloudPlatform(renamedBaseAppManifest) {
|
|
20
|
+
const sapCloudService = renamedBaseAppManifest["sap.cloud"]?.service;
|
|
21
|
+
const sapPlatformCf = renamedBaseAppManifest["sap.platform.cf"];
|
|
22
|
+
if (sapPlatformCf && sapCloudService) {
|
|
23
|
+
sapPlatformCf.oAuthScopes = sapPlatformCf.oAuthScopes.map((scope) => scope.replace(`$XSAPPNAME.`, `$XSAPPNAME('${sapCloudService}').`));
|
|
24
|
+
}
|
|
25
|
+
if (this.configuration.sapCloudService) {
|
|
26
|
+
if (renamedBaseAppManifest["sap.cloud"] == null) {
|
|
27
|
+
renamedBaseAppManifest["sap.cloud"] = {};
|
|
28
|
+
}
|
|
29
|
+
renamedBaseAppManifest["sap.cloud"].service = this.configuration.sapCloudService;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
delete renamedBaseAppManifest["sap.cloud"];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
getConfigurationType() {
|
|
36
|
+
return "cf";
|
|
37
|
+
}
|
|
38
|
+
createAppVariantHierarchyItem(appVariantId, version) {
|
|
39
|
+
return {
|
|
40
|
+
appVariantId,
|
|
41
|
+
version
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=cfProcessor.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IConfiguration } from "../model/types.js";
|
|
2
|
+
export default interface IProcessor {
|
|
3
|
+
createAppVariantHierarchyItem(appVariantId: string, version: string): void;
|
|
4
|
+
getBaseAppFiles(baseAppId: string): Promise<Map<string, string>>;
|
|
5
|
+
updateLandscapeSpecificContent(renamedBaseAppManifest: any, baseAppFiles?: Map<string, string>): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export declare function determineProcessor(configuration: IConfiguration): IProcessor;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import AbapProcessor from "./abapProcessor.js";
|
|
2
|
+
import AbapRepoManager from "../repositories/abapRepoManager.js";
|
|
3
|
+
import AnnotationManager from "../annotationManager.js";
|
|
4
|
+
import BaseAppFilesCacheManager from "../cache/baseAppFilesCacheManager.js";
|
|
5
|
+
import CFProcessor from "./cfProcessor.js";
|
|
6
|
+
export function determineProcessor(configuration) {
|
|
7
|
+
const cacheManager = new BaseAppFilesCacheManager(configuration);
|
|
8
|
+
const abapRepoManager = new AbapRepoManager(configuration);
|
|
9
|
+
const annotationManager = new AnnotationManager(configuration, abapRepoManager);
|
|
10
|
+
const processors = [
|
|
11
|
+
new CFProcessor(configuration, cacheManager),
|
|
12
|
+
new AbapProcessor(configuration, cacheManager, abapRepoManager, annotationManager)
|
|
13
|
+
];
|
|
14
|
+
let processor = processors.find(processor => processor.getConfigurationType() === configuration.type);
|
|
15
|
+
if (processor) {
|
|
16
|
+
processor.validateConfiguration();
|
|
17
|
+
return processor;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
for (const processor of processors) {
|
|
21
|
+
try {
|
|
22
|
+
processor.validateConfiguration();
|
|
23
|
+
return processor;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new Error("ui5.yaml configuration should correspond either ABAP or SAP BTP landscape");
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=processor.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IConfiguration, IMetadata } from "../model/types.js";
|
|
2
|
+
export default class AbapRepoManager {
|
|
3
|
+
private auth?;
|
|
4
|
+
private configuration;
|
|
5
|
+
constructor(configuration: IConfiguration);
|
|
6
|
+
getAnnotationMetadata(uri: string): Promise<IMetadata>;
|
|
7
|
+
downloadAnnotationFile(uri: string): Promise<Map<string, string>>;
|
|
8
|
+
getMetadata(baseAppId: string): Promise<IMetadata>;
|
|
9
|
+
downloadBaseAppFiles(): Promise<Map<string, string>>;
|
|
10
|
+
private getMetadataRequest;
|
|
11
|
+
private downloadBaseAppFilesRequest;
|
|
12
|
+
private getAuth;
|
|
13
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import RequestUtil from "../util/requestUtil.js";
|
|
2
|
+
import { getLogger } from "@ui5/logger";
|
|
3
|
+
import { unzipZipEntries } from "../util/zipUtil.js";
|
|
4
|
+
const log = getLogger("@ui5/task-adaptation::AbapRepoManager");
|
|
5
|
+
const REQUEST_OPTIONS_XML = {
|
|
6
|
+
responseType: "text",
|
|
7
|
+
headers: {
|
|
8
|
+
"Accept": "text/html,application/xhtml+xml,application/xml"
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const REQUEST_OPTIONS_JSON = {
|
|
12
|
+
headers: {
|
|
13
|
+
"Accept": "application/json"
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export default class AbapRepoManager {
|
|
17
|
+
auth;
|
|
18
|
+
configuration;
|
|
19
|
+
constructor(configuration) {
|
|
20
|
+
this.configuration = configuration;
|
|
21
|
+
}
|
|
22
|
+
async getAnnotationMetadata(uri) {
|
|
23
|
+
const header = await RequestUtil.retryWithAuth(() => RequestUtil.head(uri), () => RequestUtil.head(uri, this.getAuth()));
|
|
24
|
+
return { changedOn: header.modified };
|
|
25
|
+
}
|
|
26
|
+
async downloadAnnotationFile(uri) {
|
|
27
|
+
const annotationUri = `https://${this.configuration.destination}.dest${uri}`;
|
|
28
|
+
const annotation = await RequestUtil.retryWithAuth(() => RequestUtil.get(annotationUri, REQUEST_OPTIONS_XML), () => RequestUtil.get(annotationUri, REQUEST_OPTIONS_XML, this.getAuth()));
|
|
29
|
+
return new Map([["annotation.xml", annotation]]);
|
|
30
|
+
}
|
|
31
|
+
getMetadata(baseAppId) {
|
|
32
|
+
return RequestUtil.retryWithAuth(() => this.getMetadataRequest(baseAppId), () => this.getMetadataRequest(baseAppId, this.getAuth()));
|
|
33
|
+
}
|
|
34
|
+
downloadBaseAppFiles() {
|
|
35
|
+
return RequestUtil.retryWithAuth(() => this.downloadBaseAppFilesRequest(), () => this.downloadBaseAppFilesRequest(this.getAuth()));
|
|
36
|
+
}
|
|
37
|
+
async getMetadataRequest(id, auth) {
|
|
38
|
+
let uri = `https://${this.configuration.destination}.dest/sap/bc/ui2/app_index/ui5_app_info_json?id=${id}`;
|
|
39
|
+
const data = await RequestUtil.get(uri, REQUEST_OPTIONS_JSON, auth);
|
|
40
|
+
if (data && data[id]) {
|
|
41
|
+
return {
|
|
42
|
+
changedOn: data[id].url,
|
|
43
|
+
id
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
log.warn(`UI5AppInfoJson request doesn't contain cache buster token for sap.app/id '${id}'. Fallback to download.`);
|
|
47
|
+
}
|
|
48
|
+
async downloadBaseAppFilesRequest(auth) {
|
|
49
|
+
const { destination, appName } = this.configuration;
|
|
50
|
+
const encodedAppName = encodeURIComponent(appName);
|
|
51
|
+
const uri = `https://${destination}.dest/sap/opu/odata/UI5/ABAP_REPOSITORY_SRV/Repositories('${encodedAppName}')?DownloadFiles=RUNTIME&CodePage=UTF8`;
|
|
52
|
+
const data = await RequestUtil.get(uri, {}, auth);
|
|
53
|
+
if (data?.d?.ZipArchive.length > 0) {
|
|
54
|
+
const buffer = Buffer.from(data.d.ZipArchive, "base64");
|
|
55
|
+
return unzipZipEntries(buffer);
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`App '${appName}' from destination '${destination}' doesn't contain files`);
|
|
58
|
+
}
|
|
59
|
+
getAuth() {
|
|
60
|
+
if (!this.auth) {
|
|
61
|
+
if (this.configuration?.credentials) {
|
|
62
|
+
let { username, password } = this.configuration?.credentials;
|
|
63
|
+
if (username && password) {
|
|
64
|
+
const ENV_PREFIX = "env:";
|
|
65
|
+
username = username.substring(ENV_PREFIX.length);
|
|
66
|
+
password = password.substring(ENV_PREFIX.length);
|
|
67
|
+
if (process.env[username] && process.env[password]) {
|
|
68
|
+
this.auth = {
|
|
69
|
+
username: process.env[username],
|
|
70
|
+
password: process.env[password]
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!this.auth) {
|
|
77
|
+
throw new Error("Please provide ABAP System credentials in .env file of the project: https://help.sap.com/docs/SAP_FIORI_tools/17d50220bcd848aa854c9c182d65b699/1c859274b511435ab6bd45f70e7f9af2.html.");
|
|
78
|
+
}
|
|
79
|
+
return this.auth;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=abapRepoManager.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IConfiguration } from "./../model/types.js";
|
|
2
|
+
export default class HTML5RepoManager {
|
|
3
|
+
static getBaseAppFiles(configuration: IConfiguration): Promise<Map<string, string>>;
|
|
4
|
+
static getMetadata(configuration: IConfiguration): Promise<any>;
|
|
5
|
+
private static getHtml5RepoInfo;
|
|
6
|
+
private static getHTML5Credentials;
|
|
7
|
+
private static getToken;
|
|
8
|
+
private static requestMetadata;
|
|
9
|
+
private static getBaseAppZipEntries;
|
|
10
|
+
private static download;
|
|
11
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import CFUtil from "./../util/cfUtil.js";
|
|
2
|
+
import RequestUtil from "./../util/requestUtil.js";
|
|
3
|
+
import { getLogger } from "@ui5/logger";
|
|
4
|
+
import { unzipZipEntries } from "./../util/zipUtil.js";
|
|
5
|
+
const log = getLogger("@ui5/task-adaptation::HTML5RepoManager");
|
|
6
|
+
export default class HTML5RepoManager {
|
|
7
|
+
static async getBaseAppFiles(configuration) {
|
|
8
|
+
const { token, baseUri } = await this.getHtml5RepoInfo(configuration);
|
|
9
|
+
return this.getBaseAppZipEntries(configuration, baseUri, token);
|
|
10
|
+
}
|
|
11
|
+
static async getMetadata(configuration) {
|
|
12
|
+
const { token, baseUri } = await this.getHtml5RepoInfo(configuration);
|
|
13
|
+
return this.requestMetadata(configuration, baseUri, token);
|
|
14
|
+
}
|
|
15
|
+
static async getHtml5RepoInfo(configuration) {
|
|
16
|
+
const spaceGuid = await CFUtil.getSpaceGuid(configuration?.spaceGuid);
|
|
17
|
+
const credentials = await this.getHTML5Credentials(spaceGuid);
|
|
18
|
+
const token = await this.getToken(credentials);
|
|
19
|
+
return {
|
|
20
|
+
token,
|
|
21
|
+
baseUri: credentials.uri
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
static async getHTML5Credentials(spaceGuid) {
|
|
25
|
+
log.verbose("Getting HTML5 Repo Runtime credentials from space " + spaceGuid);
|
|
26
|
+
const PLAN_NAME = "app-runtime";
|
|
27
|
+
const SERVIСE_INSTANCE_NAME = "html5-apps-repo-runtime";
|
|
28
|
+
const getParams = {
|
|
29
|
+
spaceGuids: [spaceGuid],
|
|
30
|
+
planNames: [PLAN_NAME],
|
|
31
|
+
names: [SERVIСE_INSTANCE_NAME]
|
|
32
|
+
};
|
|
33
|
+
const createParams = {
|
|
34
|
+
spaceGuid,
|
|
35
|
+
planName: PLAN_NAME,
|
|
36
|
+
serviceName: SERVIСE_INSTANCE_NAME,
|
|
37
|
+
tags: ["html5-apps-repo-rt"]
|
|
38
|
+
};
|
|
39
|
+
const serviceKeys = await CFUtil.getServiceInstanceKeys(getParams, createParams);
|
|
40
|
+
return serviceKeys.credentials;
|
|
41
|
+
}
|
|
42
|
+
static async getToken({ uaa }) {
|
|
43
|
+
log.verbose("Getting HTML5 Repo token");
|
|
44
|
+
const auth = Buffer.from(uaa.clientid + ":" + uaa.clientsecret);
|
|
45
|
+
const options = {
|
|
46
|
+
headers: {
|
|
47
|
+
"Content-Type": "application/json",
|
|
48
|
+
"Authorization": "Basic " + auth.toString("base64")
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const uri = `${uaa.url}/oauth/token?grant_type=client_credentials`;
|
|
52
|
+
return RequestUtil.get(uri, options).then((json) => json["access_token"]);
|
|
53
|
+
}
|
|
54
|
+
static async requestMetadata(options, html5RepoBaseUri, token) {
|
|
55
|
+
const { appHostId, appName, appVersion } = options;
|
|
56
|
+
const uri = `${html5RepoBaseUri}/applications/metadata/`;
|
|
57
|
+
const requestOptions = {
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "application/json",
|
|
60
|
+
"Authorization": "Bearer " + token,
|
|
61
|
+
"x-app-host-id": appHostId
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const metadata = await RequestUtil.get(uri, requestOptions);
|
|
65
|
+
return metadata.find((item) => item.appHostId === appHostId && item.applicationName === appName && item.applicationVersion === appVersion);
|
|
66
|
+
}
|
|
67
|
+
static async getBaseAppZipEntries(options, html5RepoBaseUri, token) {
|
|
68
|
+
const { appHostId, appName, appVersion } = options;
|
|
69
|
+
const uri = `${html5RepoBaseUri}/applications/content/${appName}-${appVersion}/`;
|
|
70
|
+
const zip = await this.download(token, appHostId, uri);
|
|
71
|
+
return unzipZipEntries(zip);
|
|
72
|
+
}
|
|
73
|
+
static async download(token, appHostId, uri) {
|
|
74
|
+
if (!token) {
|
|
75
|
+
throw new Error("HTML5 token is undefined");
|
|
76
|
+
}
|
|
77
|
+
return RequestUtil.get(uri, {
|
|
78
|
+
responseType: "arraybuffer",
|
|
79
|
+
headers: {
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
"Authorization": "Bearer " + token,
|
|
82
|
+
"x-app-host-id": appHostId
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=html5RepoManager.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ICreateServiceInstanceParams, IGetServiceInstanceParams, IResource, IServiceKeys } from "../model/types.js";
|
|
2
|
+
export default class CFUtil {
|
|
3
|
+
/**
|
|
4
|
+
* Get or create service keys for service instance found by query
|
|
5
|
+
* @static
|
|
6
|
+
* @param {IGetServiceInstanceParams} getServiceInstanceParams query parameters to find a service instance by
|
|
7
|
+
* @param {ICreateServiceInstanceParams} [createServiceInstanceParams] parameters to create a service instance
|
|
8
|
+
* @return {Promise<IServiceKeys>} promise with service keys
|
|
9
|
+
* @memberof CFUtil
|
|
10
|
+
*/
|
|
11
|
+
static getServiceInstanceKeys(getServiceInstanceParams: IGetServiceInstanceParams, createServiceInstanceParams?: ICreateServiceInstanceParams): Promise<IServiceKeys>;
|
|
12
|
+
static createService(params: ICreateServiceInstanceParams): Promise<void>;
|
|
13
|
+
private static getOrCreateServiceKeys;
|
|
14
|
+
private static getServiceKeys;
|
|
15
|
+
private static createServiceKey;
|
|
16
|
+
private static getServiceInstance;
|
|
17
|
+
static requestCfApi(url: string): Promise<IResource[]>;
|
|
18
|
+
static getOAuthToken(): Promise<string>;
|
|
19
|
+
private static cfExecute;
|
|
20
|
+
private static errorsToString;
|
|
21
|
+
private static parseJson;
|
|
22
|
+
/**
|
|
23
|
+
* Get space guid from configuration or local CF fodler
|
|
24
|
+
* @static
|
|
25
|
+
* @param {string} spaceGuid ui5.yaml options
|
|
26
|
+
* @return {Promise<string>} promise with space guid
|
|
27
|
+
* @memberof CFUtil
|
|
28
|
+
*/
|
|
29
|
+
static getSpaceGuid(spaceGuid?: string): Promise<string>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { cfCreateService, cfGetInstanceCredentials, cfGetTarget } from "@sap/cf-tools/out/src/cf-local.js";
|
|
2
|
+
import { Cli } from "@sap/cf-tools/out/src/cli.js";
|
|
3
|
+
import { eFilters } from "@sap/cf-tools/out/src/types.js";
|
|
4
|
+
import { getLogger } from "@ui5/logger";
|
|
5
|
+
const log = getLogger("@ui5/task-adaptation::CFUtil");
|
|
6
|
+
export default class CFUtil {
|
|
7
|
+
/**
|
|
8
|
+
* Get or create service keys for service instance found by query
|
|
9
|
+
* @static
|
|
10
|
+
* @param {IGetServiceInstanceParams} getServiceInstanceParams query parameters to find a service instance by
|
|
11
|
+
* @param {ICreateServiceInstanceParams} [createServiceInstanceParams] parameters to create a service instance
|
|
12
|
+
* @return {Promise<IServiceKeys>} promise with service keys
|
|
13
|
+
* @memberof CFUtil
|
|
14
|
+
*/
|
|
15
|
+
static async getServiceInstanceKeys(getServiceInstanceParams, createServiceInstanceParams) {
|
|
16
|
+
let serviceInstances = await this.getServiceInstance(getServiceInstanceParams);
|
|
17
|
+
if (!(serviceInstances?.length > 0) && createServiceInstanceParams) {
|
|
18
|
+
await this.createService(createServiceInstanceParams);
|
|
19
|
+
serviceInstances = await this.getServiceInstance(getServiceInstanceParams);
|
|
20
|
+
}
|
|
21
|
+
if (!(serviceInstances?.length > 0)) {
|
|
22
|
+
throw new Error(`Cannot find '${getServiceInstanceParams.names?.join(", ")}' service in current space: ${getServiceInstanceParams.spaceGuids?.join(", ")}`);
|
|
23
|
+
}
|
|
24
|
+
// we can use any instance in the list to connect to HTML5 Repo
|
|
25
|
+
log.verbose(`Use '${serviceInstances[0].name}' HTML5 Repo Runtime service instance`);
|
|
26
|
+
const serviceKeys = await this.getOrCreateServiceKeys(serviceInstances[0]);
|
|
27
|
+
if (!(serviceKeys?.length > 0)) {
|
|
28
|
+
throw new Error(`Cannot get service keys for '${getServiceInstanceParams.names?.join(", ")}' service in current space: ${getServiceInstanceParams.spaceGuids?.join(", ")}`);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
credentials: serviceKeys[0].credentials,
|
|
32
|
+
serviceInstance: serviceInstances[0]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
static async createService(params) {
|
|
36
|
+
log.verbose(`Creating a service instance with parameters: ${JSON.stringify(params)}`);
|
|
37
|
+
const resources = await this.requestCfApi(`/v3/service_plans?names=${params.planName}&space_guids=${params.spaceGuid}`);
|
|
38
|
+
const publicPlan = resources.find(resource => resource.visibility_type === "public");
|
|
39
|
+
if (!publicPlan) {
|
|
40
|
+
throw new Error(`Cannot find a public plan by name '${params.serviceName}' in space '${params.spaceGuid}'`);
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
await cfCreateService(publicPlan.guid, params.serviceName, params.parameters, params.tags);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new Error(`Cannot create a service instance '${params.serviceName}' in space '${params.spaceGuid}': ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
static async getOrCreateServiceKeys(serviceInstance) {
|
|
50
|
+
const credentials = await this.getServiceKeys(serviceInstance.guid);
|
|
51
|
+
if (credentials.length === 0) {
|
|
52
|
+
const serviceKeyName = serviceInstance.name + "_key";
|
|
53
|
+
log.info(`Creating service key '${serviceKeyName}' for service instance '${serviceInstance.name}'`);
|
|
54
|
+
await this.createServiceKey(serviceInstance.name, serviceKeyName);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
return credentials;
|
|
58
|
+
}
|
|
59
|
+
return this.getServiceKeys(serviceInstance.guid);
|
|
60
|
+
}
|
|
61
|
+
static getServiceKeys(serviceInstanceGuid) {
|
|
62
|
+
return cfGetInstanceCredentials({
|
|
63
|
+
filters: [{
|
|
64
|
+
value: serviceInstanceGuid,
|
|
65
|
+
key: eFilters.service_instance_guids
|
|
66
|
+
}]
|
|
67
|
+
}).catch((error) => {
|
|
68
|
+
throw new Error("Failed to get service credentials: " + error.message);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
static async createServiceKey(serviceInstanceName, serviceKeyName) {
|
|
72
|
+
try {
|
|
73
|
+
return this.cfExecute(["create-service-key", serviceInstanceName, serviceKeyName]);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
throw new Error(`Couldn't create a service key for instance: ${serviceInstanceName}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
static async getServiceInstance(params) {
|
|
80
|
+
const PARAM_MAP = {
|
|
81
|
+
spaceGuids: "space_guids",
|
|
82
|
+
planNames: "service_plan_names",
|
|
83
|
+
names: "names"
|
|
84
|
+
};
|
|
85
|
+
const parameters = Object.entries(params)
|
|
86
|
+
.filter(([, value]) => value?.length && value?.length > 0)
|
|
87
|
+
.map(([key, value]) => `${PARAM_MAP[key]}=${value?.join(",")}`);
|
|
88
|
+
const uri = `/v3/service_instances` + (parameters.length > 0 ? `?${parameters.join("&")}` : "");
|
|
89
|
+
const resources = await this.requestCfApi(uri);
|
|
90
|
+
return resources.map((service) => ({
|
|
91
|
+
name: service.name,
|
|
92
|
+
guid: service.guid
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
static async requestCfApi(url) {
|
|
96
|
+
const response = await this.cfExecute(["curl", url]);
|
|
97
|
+
const json = this.parseJson(response);
|
|
98
|
+
const resources = json?.resources;
|
|
99
|
+
const totalPages = json?.pagination?.total_pages;
|
|
100
|
+
if (totalPages > 1) {
|
|
101
|
+
const pages = Array.from({ length: totalPages - 1 }, (_, i) => i + 2);
|
|
102
|
+
return resources.concat(await Promise.all(pages.map(async (page) => {
|
|
103
|
+
const uri = `${url}${url.includes("?") ? "&" : "?"}page=${page}`;
|
|
104
|
+
const response = await this.cfExecute(["curl", uri]);
|
|
105
|
+
return this.parseJson(response)?.resources || [];
|
|
106
|
+
})).then(resources => [].concat(...resources)));
|
|
107
|
+
}
|
|
108
|
+
return resources;
|
|
109
|
+
}
|
|
110
|
+
static getOAuthToken() {
|
|
111
|
+
return this.cfExecute(["oauth-token"]);
|
|
112
|
+
}
|
|
113
|
+
static async cfExecute(params) {
|
|
114
|
+
const MAX_ATTEMPTS = 3;
|
|
115
|
+
const errors = new Set();
|
|
116
|
+
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
117
|
+
try {
|
|
118
|
+
const response = await Cli.execute(params, { env: { "CF_COLOR": "false" } });
|
|
119
|
+
if (response.exitCode === 0) {
|
|
120
|
+
const errorValues = [...errors.values()];
|
|
121
|
+
if (errorValues?.length > 0) {
|
|
122
|
+
log.verbose(this.errorsToString(errorValues));
|
|
123
|
+
}
|
|
124
|
+
return response.stdout;
|
|
125
|
+
}
|
|
126
|
+
errors.add(response.error || response.stderr);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
errors.add(error.message);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
throw new Error(`Failed to send request with parameters '${JSON.stringify(params)}': ${this.errorsToString([...errors.values()])}`);
|
|
133
|
+
}
|
|
134
|
+
static errorsToString(errors) {
|
|
135
|
+
return errors.length > 1
|
|
136
|
+
? errors.map((error, attempt) => `${attempt + 1} attempt: ${error}`).join("; ")
|
|
137
|
+
: errors.map(error => error);
|
|
138
|
+
}
|
|
139
|
+
static parseJson(jsonString) {
|
|
140
|
+
try {
|
|
141
|
+
return JSON.parse(jsonString);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
throw new Error(`Failed parse response from request CF API: ${error.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get space guid from configuration or local CF fodler
|
|
149
|
+
* @static
|
|
150
|
+
* @param {string} spaceGuid ui5.yaml options
|
|
151
|
+
* @return {Promise<string>} promise with space guid
|
|
152
|
+
* @memberof CFUtil
|
|
153
|
+
*/
|
|
154
|
+
static async getSpaceGuid(spaceGuid) {
|
|
155
|
+
if (spaceGuid == null) {
|
|
156
|
+
const spaceName = (await cfGetTarget())?.space;
|
|
157
|
+
if (spaceName) {
|
|
158
|
+
const resources = await this.requestCfApi(`/v3/spaces?names=${spaceName}`);
|
|
159
|
+
for (const resource of resources) {
|
|
160
|
+
spaceGuid = resource.guid;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (spaceGuid == null) {
|
|
166
|
+
throw new Error("Please login to Cloud Foundry with 'cf login' and try again");
|
|
167
|
+
}
|
|
168
|
+
return spaceGuid;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=cfUtil.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IConfiguration } from "../model/types.js";
|
|
2
|
+
import Language from "../model/language.js";
|
|
3
|
+
export declare function dotToUnderscore(value: string): string;
|
|
4
|
+
export declare function validateObject<T extends Object>(options: T, properties: Array<keyof T>, message: string): void;
|
|
5
|
+
export declare function escapeRegex(update: string): string;
|
|
6
|
+
export declare function renameResources(files: Map<string, string>, search: string, replacement: string): Map<string, string>;
|
|
7
|
+
export declare function insertInArray<T>(array: T[], index: number, insert: T): void;
|
|
8
|
+
export declare function writeTempAnnotations({ writeTempFiles }: IConfiguration, name: string, language: Language, content: string): void;
|
|
9
|
+
export declare function removePropertiesExtension(filePath: string): string;
|
|
10
|
+
export declare function traverse(json: any, paths: string[], callback: (json: any, key: string | number, paths: string[]) => void): void;
|
|
11
|
+
export declare function logBuilderVersion(): void;
|
|
12
|
+
export declare function logBetaUsage(): void;
|
|
13
|
+
export declare function getUniqueName(existingNames: string[], template: string): string;
|