Skip to content

Commit 237371b

Browse files
Add external service support to headless generator (#4576)
* wip * wip * wip * Linting auto fix commit * changeset and clean up * clean up * fix tests * test order * throw errors on try catches for the file path reads and parsing. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 4015f77 commit 237371b

30 files changed

Lines changed: 3876 additions & 5 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@sap-ux/axios-extension": patch
3+
"@sap-ux/fiori-generator-shared": patch
4+
"@sap-ux/fiori-app-sub-generator": patch
5+
---
6+
7+
fix(axios-extension): export EntitySetData type
8+
feat(fiori-generator-shared): add ExternalServiceConfig headless type supporting metadata and entityData as inline values or file paths
9+
feat(fiori-app-sub-generator): resolve external service metadata and entityData file paths in headless generator before passing to writer

packages/axios-extension/src/abap/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@ export {
3636
CodeListService,
3737
ValueListService,
3838
ExternalServiceReference,
39-
ExternalService
39+
ExternalService,
40+
EntitySetData
4041
} from './types';

packages/fiori-app-sub-generator/src/app-headless/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { FioriAppGeneratorOptions } from '../fiori-app-generator';
44
import { APP_GENERATOR_MODULE, FioriAppGenerator } from '../fiori-app-generator';
55
import { initI18nFioriAppSubGenerator, t } from '../utils/i18n';
66
import { transformExtState } from './transforms';
7+
import { resolveExternalServices } from './resolve';
78

89
/**
910
* Generator that allows the creation of a Fiori applications without prompting based on a provided app config.
@@ -24,7 +25,11 @@ export class FioriAppGeneratorHeadless extends FioriAppGenerator {
2425
);
2526

2627
try {
27-
this.state = transformExtState(this.options.appConfig);
28+
const appConfig = this.options.appConfig;
29+
if (appConfig.service?.externalServices) {
30+
appConfig.service.externalServices = resolveExternalServices(appConfig.service.externalServices);
31+
}
32+
this.state = transformExtState(appConfig);
2833
} catch (error) {
2934
this.log(t('logMessages.generatorExiting'));
3035
this.env.error(error);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import type { EntitySetData, ExternalService } from '@sap-ux/axios-extension';
2+
import type { ExternalServiceConfig } from '@sap-ux/fiori-generator-shared';
3+
import { existsSync, readFileSync } from 'node:fs';
4+
import { isAbsolute, resolve } from 'node:path';
5+
6+
/**
7+
* Returns inline XML, or reads and returns the contents of the file at the given path.
8+
*
9+
* @param metadata The metadata XML string or the path to the metadata file.
10+
*/
11+
export function resolveMetadata(metadata: string): string {
12+
if (metadata.trimStart().startsWith('<')) {
13+
return metadata;
14+
}
15+
const filePath = isAbsolute(metadata) ? metadata : resolve(process.cwd(), metadata);
16+
if (!existsSync(filePath)) {
17+
throw new Error(`Metadata file not found: ${filePath}`);
18+
}
19+
try {
20+
return readFileSync(filePath, 'utf-8');
21+
} catch (error) {
22+
throw new Error(`Failed to read metadata file: ${filePath}. ${error instanceof Error ? error.message : error}`);
23+
}
24+
}
25+
26+
/**
27+
* Returns an inline array, or reads and parses the JSON file at the given path.
28+
*
29+
* @param entityData The entity data array or the path to the JSON file.
30+
*/
31+
export function resolveEntityData(entityData: EntitySetData[] | string): EntitySetData[] {
32+
if (Array.isArray(entityData)) {
33+
return entityData;
34+
}
35+
const filePath = isAbsolute(entityData) ? entityData : resolve(process.cwd(), entityData);
36+
if (!existsSync(filePath)) {
37+
throw new Error(`Entity data file not found: ${filePath}`);
38+
}
39+
try {
40+
return JSON.parse(readFileSync(filePath, 'utf-8'));
41+
} catch (error) {
42+
throw new Error(
43+
`Failed to read or parse entity data file: ${filePath}. ${error instanceof Error ? error.message : error}`
44+
);
45+
}
46+
}
47+
48+
/**
49+
* Resolves metadata and entityData entries based on their content.
50+
*
51+
* @param services External service configurations.
52+
*/
53+
export function resolveExternalServices(services: ExternalServiceConfig[]): ExternalService[] {
54+
return services.map(
55+
(entry) =>
56+
({
57+
...entry,
58+
metadata: resolveMetadata(entry.metadata),
59+
entityData: entry.entityData ? resolveEntityData(entry.entityData) : undefined
60+
}) as ExternalService
61+
);
62+
}

packages/fiori-app-sub-generator/src/app-headless/transforms.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ function _setServiceDefaults(floorplan: AppConfig['floorplan'], service?: AppCon
145145
servicePath: service?.servicePath,
146146
client: service?.client,
147147
edmx: service?.edmx,
148-
version
148+
version,
149+
valueListMetadata: service?.externalServices
149150
} as Service;
150151

151152
if (service?.destination) {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"generationParameters": {
3+
"generationDate": "Fri Apr 17 2026 13:29:25 GMT+0100 (Irish Standard Time)",
4+
"generatorPlatform": "Visual Studio Code",
5+
"serviceType": "OData URL",
6+
"metadataFilename": "",
7+
"serviceUrl": "https://sap-ux-mock-services-v4-feop.cfapps.us10.hana.ondemand.com/sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001/",
8+
"appName": "lrop_v4_with_vh",
9+
"appTitle": "Project's \"Title\"",
10+
"appDescription": "Test 'Project' \"Description\"",
11+
"appNamespace": "testNameSpace",
12+
"ui5Theme": "sap_fiori_3",
13+
"ui5Version": "1.84.0",
14+
"enableEslint": false,
15+
"enableTypeScript": false,
16+
"showMockDataInfo": true,
17+
"generatorVersion": "0.0.0",
18+
"template": "List Report Page V4",
19+
"generatorName": "SAP Fiori Application Generator",
20+
"entityRelatedConfig": [
21+
{
22+
"type": "Main Entity",
23+
"value": "Travel"
24+
},
25+
{
26+
"type": "Navigation Entity",
27+
"value": "_Booking"
28+
}
29+
],
30+
"launchText": "To launch the generated application, run the following from the generated application root folder:\n\n```\n npm start\n```",
31+
"valueHelpDownloaded": true
32+
}
33+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
node_modules/
2+
dist/
3+
.scp/
4+
.env
5+
Makefile*.mta
6+
mta_archives
7+
mta-*
8+
resources
9+
archive.zip
10+
.*_mta_build_tmp
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## Application Details
2+
| |
3+
| ------------- |
4+
|**Generation Date and Time**<br>Fri Apr 17 2026 13:29:25 GMT+0100 (Irish Standard Time)|
5+
|**App Generator**<br>SAP Fiori Application Generator|
6+
|**App Generator Version**<br>0.0.0|
7+
|**Generation Platform**<br>Visual Studio Code|
8+
|**Template Used**<br>List Report Page V4|
9+
|**Service Type**<br>OData URL|
10+
|**Service URL**<br>https://sap-ux-mock-services-v4-feop.cfapps.us10.hana.ondemand.com/sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001/|
11+
|**Module Name**<br>lrop_v4_with_vh|
12+
|**Application Title**<br>Project&#39;s &#34;Title&#34;|
13+
|**Namespace**<br>testNameSpace|
14+
|**UI5 Theme**<br>sap_fiori_3|
15+
|**UI5 Version**<br>1.84.0|
16+
|**Enable TypeScript**<br>False|
17+
|**Add Eslint configuration**<br>False|
18+
|**Value Help Metadata**<br>Downloaded for external services|
19+
|**Main Entity**<br>Travel|
20+
|**Navigation Entity**<br>_Booking|
21+
22+
## lrop_v4_with_vh
23+
24+
Test &#39;Project&#39; &#34;Description&#34;
25+
26+
### Starting the generated app
27+
28+
- This app has been generated using the SAP Fiori tools - App Generator, as part of the SAP Fiori tools suite. To launch the generated application, run the following from the generated application root folder:
29+
30+
```
31+
npm start
32+
```
33+
34+
- It is also possible to run the application using mock data that reflects the OData Service URL supplied during application generation. In order to run the application with Mock Data, run the following from the generated app root folder:
35+
36+
```
37+
npm run start-mock
38+
```
39+
40+
#### Pre-requisites:
41+
42+
1. Active NodeJS LTS (Long Term Support) version and associated supported NPM version. (See https://nodejs.org)
43+
44+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "lrop_v4_with_vh",
3+
"version": "0.0.1",
4+
"description": "Test &#39;Project&#39; &#34;Description&#34;",
5+
"keywords": [
6+
"ui5",
7+
"openui5",
8+
"sapui5"
9+
],
10+
"main": "webapp/index.html",
11+
"dependencies": {},
12+
"devDependencies": {
13+
"@ui5/cli": "^4.0.33",
14+
"@sap/ux-ui5-tooling": "1",
15+
"@sap-ux/ui5-middleware-fe-mockserver": "2"
16+
},
17+
"scripts": {
18+
"start": "fiori run --open \"/test/flpSandbox.html?sap-ui-xx-viewCache=false#testNameSpacelropv4withvh-tile\"",
19+
"start-local": "fiori run --config ./ui5-local.yaml --open \"/test/flpSandbox.html?sap-ui-xx-viewCache=false#testNameSpacelropv4withvh-tile\"",
20+
"build": "ui5 build --config=ui5.yaml --clean-dest --dest dist",
21+
"start-mock": "fiori run --config ./ui5-mock.yaml --open \"/test/flpSandbox.html?sap-ui-xx-viewCache=false#testNameSpacelropv4withvh-tile\"",
22+
"deploy": "fiori verify",
23+
"deploy-config": "fiori add deploy-config",
24+
"start-noflp": "fiori run --open \"/index.html?sap-ui-xx-viewCache=false\"",
25+
"int-test": "fiori run --config ./ui5-mock.yaml --open \"/test/integration/opaTests.qunit.html\"",
26+
"start-variants-management": "fiori run --open \"/preview.html?sap-ui-xx-viewCache=false&fiori-tools-rta-mode=true&sap-ui-rta-skip-flex-validation=true#testNameSpacelropv4withvh-tile\""
27+
},
28+
"sapuxLayer": "CUSTOMER_BASE",
29+
"sapux": true
30+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
2+
3+
specVersion: "4.0"
4+
metadata:
5+
name: testNameSpace.lropv4withvh
6+
type: application
7+
framework:
8+
name: SAPUI5
9+
version: 1.82.2
10+
libraries:
11+
- name: sap.m
12+
- name: sap.ui.core
13+
- name: sap.fe.templates
14+
- name: sap.ushell
15+
- name: themelib_sap_fiori_3
16+
server:
17+
customMiddleware:
18+
- name: fiori-tools-appreload
19+
afterMiddleware: compression
20+
configuration:
21+
port: 35729
22+
path: webapp
23+
delay: 300
24+
- name: fiori-tools-preview
25+
afterMiddleware: fiori-tools-appreload
26+
configuration:
27+
flp:
28+
theme: sap_fiori_3
29+
path: test/flpSandbox.html
30+
intent:
31+
object: testNameSpacelropv4withvh
32+
action: tile
33+
- name: fiori-tools-proxy
34+
afterMiddleware: compression
35+
configuration:
36+
ignoreCertErrors: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted
37+
backend:
38+
- path: /sap
39+
url: https://sap-ux-mock-services-v4-feop.cfapps.us10.hana.ondemand.com
40+
- name: sap-fe-mockserver
41+
beforeMiddleware: csp
42+
configuration:
43+
mountPath: /
44+
services:
45+
- urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001
46+
metadataPath: ./webapp/localService/mainService/metadata.xml
47+
mockdataPath: ./webapp/localService/mainService/data
48+
generateMockData: true
49+
resolveExternalServiceReferences: true
50+
annotations: []

0 commit comments

Comments
 (0)