Skip to content

Commit

Permalink
feat(preview-middleware): legacy free ui5 version handling (#2955)
Browse files Browse the repository at this point in the history
* legacy free ui5 version handling

* add unit tests

---------

Co-authored-by: Johannes Kolbe <[email protected]>
  • Loading branch information
heimwege and johannes-kolbe authored Feb 26, 2025
1 parent 704b8b9 commit 1d4ba46
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 14 deletions.
6 changes: 6 additions & 0 deletions .changeset/red-jokes-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sap-ux-private/preview-middleware-client': patch
'@sap-ux/preview-middleware': patch
---

feat: handling of legacy free ui5 version
9 changes: 6 additions & 3 deletions packages/preview-middleware-client/src/utils/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type Ui5VersionInfo = {
major: number;
minor: number;
patch?: number;
label?: string;
};

/**
Expand Down Expand Up @@ -51,11 +52,13 @@ export async function getUi5Version(library: string = 'sap.ui.core'): Promise<Ui
version = '1.121.0';
}
const [major, minor, patch] = version.split('.').map((versionPart) => parseInt(versionPart, 10));
const label = version.split(/-(.*)/s)?.[1];

return {
major: major,
minor: minor,
patch: patch
major,
minor,
patch,
label
} satisfies Ui5VersionInfo;
}

Expand Down
34 changes: 23 additions & 11 deletions packages/preview-middleware/src/base/flp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ type OnChangeRequestHandler = (
logger: Logger
) => Promise<void>;

type Ui5Version = {
major: number;
minor: number;
patch: number;
label?: string;
};

/**
* Class handling preview of a sandbox FLP.
*/
Expand Down Expand Up @@ -222,7 +229,7 @@ export class FlpSandbox {
if (ui5Version.major === 1 && ui5Version.minor <= 71) {
this.removeAsyncHintsRequests();
}
return render(this.getSandboxTemplate(ui5Version.major), config);
return render(this.getSandboxTemplate(ui5Version), config);
}

/**
Expand Down Expand Up @@ -376,7 +383,7 @@ export class FlpSandbox {
req.headers.host,
'ui5-patched-router' in req ? req['ui5-patched-router']?.baseUrl : undefined
);
const html = render(this.getSandboxTemplate(ui5Version.major), this.templateConfig);
const html = render(this.getSandboxTemplate(ui5Version), this.templateConfig);
this.sendResponse(res, 'text/html', 200, html);
}
}
Expand Down Expand Up @@ -415,7 +422,7 @@ export class FlpSandbox {
protocol: Request['protocol'],
host: Request['headers']['host'],
baseUrl: string = ''
): Promise<{ major: number; minor: number }> {
): Promise<Ui5Version> {
let version: string | undefined;
if (!host) {
this.logger.error('Unable to fetch UI5 version: No host found in request header.');
Expand All @@ -434,25 +441,30 @@ export class FlpSandbox {
this.logger.error('Could not get UI5 version of application. Using 1.121.0 as fallback.');
version = '1.121.0';
}
const [major, minor] = version.split('.').map((versionPart) => parseInt(versionPart, 10));
const [major, minor, patch] = version.split('.').map((versionPart) => parseInt(versionPart, 10));
const label = version.split(/-(.*)/s)?.[1];
return {
major,
minor
minor,
patch,
label
};
}

/**
* Read the sandbox template file based on the given UI5 version.
*
* @param ui5MajorVersion - the major version of UI5
* @param ui5Version - the UI5 version
* @returns the template for the sandbox HTML file
*/
private getSandboxTemplate(ui5MajorVersion: number): string {
this.logger.info(`Using sandbox template for UI5 major version ${ui5MajorVersion}.`);
return readFileSync(
join(__dirname, `../../templates/flp/sandbox${ui5MajorVersion === 1 ? '' : ui5MajorVersion}.html`),
'utf-8'
private getSandboxTemplate(ui5Version: Ui5Version): string {
this.logger.info(
`Using sandbox template for UI5 version ${ui5Version.major}.${ui5Version.minor}.${ui5Version.patch}${
ui5Version.label ? `-${ui5Version.label}` : ''
}.`
);
const filePrefix = ui5Version.major > 1 || ui5Version.label?.includes('legacy-free') ? '2' : '';
return readFileSync(join(__dirname, `../../templates/flp/sandbox${filePrefix}.html`), 'utf-8');
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1593,6 +1593,154 @@ exports[`FlpSandbox router test/flp.html UI5 2.x 1`] = `
</script>
</head>
<!-- UI Content -->
<body class=\\"sapUiBody\\" id=\\"content\\">
</body>
</html>"
`;
exports[`FlpSandbox router test/flp.html UI5 legacy-free 1`] = `
"<!DOCTYPE HTML>
<html lang=\\"en\\">
<!-- Copyright (c) 2015 SAP AG, All Rights Reserved -->
<head>
<meta http-equiv=\\"X-UA-Compatible\\" content=\\"IE=edge\\">
<meta charset=\\"UTF-8\\">
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\">
<title>Local FLP Sandbox</title>
<!-- Bootstrap the unified shell in sandbox mode for standalone usage.
The renderer is specified in the global Unified Shell configuration object \\"sap-ushell-config\\".
The fiori2 renderer will render the shell header allowing, for instance,
testing of additional application setting buttons.
The navigation target resolution service is configured in a way that the empty URL hash is
resolved to our own application.
This example uses relative path references for the SAPUI5 resources and test-resources;
it might be necessary to adapt them depending on the target runtime platform.
The sandbox platform is restricted to development or demo use cases and must NOT be used
for productive scenarios.
-->
<script type=\\"text/javascript\\">
window[\\"sap-ushell-config\\"] = {
defaultRenderer: \\"fiori2\\",
renderers: {
fiori2: {
componentData: {
config: {
search: \\"hidden\\",
enableSearch: false
}
}
}
},
applications: {\\"app-preview\\":{\\"title\\":\\"My Simple App\\",\\"description\\":\\"This is a very simple application.\\",\\"additionalInformation\\":\\"SAPUI5.Component=test.fe.v2.app\\",\\"applicationType\\":\\"URL\\",\\"url\\":\\"..\\"},\\"testfev2other-preview\\":{\\"title\\":\\"My Other App\\",\\"description\\":\\"This is a very simple application.\\",\\"additionalInformation\\":\\"SAPUI5.Component=test.fe.v2.other\\",\\"applicationType\\":\\"URL\\",\\"url\\":\\"/yet/another/app\\"}}
};
</script>
<script type=\\"text/javascript\\">
window[\\"data-open-ux-preview-basePath\\"] = \\"..\\";
</script>
<script src=\\"../resources/sap/ushell/bootstrap/sandbox2.js\\" id=\\"sap-ushell-bootstrap\\"></script>
<!-- Bootstrap the UI5 core library. 'data-sap-ui-frameOptions=\\"allow\\"' is a NON-SECURE setting for test environments -->
<script id=\\"sap-ui-bootstrap\\"
src=\\"../resources/sap-ui-core.js\\"
data-sap-ui-libs=\\"sap.m,sap.ui.core,sap.ushell,sap.f,sap.ui.comp,sap.ui.generic.app,sap.suite.ui.generic.template\\"
data-sap-ui-async=\\"true\\"
data-sap-ui-theme=\\"sap_horizon_dark\\"
data-sap-ui-compat-version=\\"edge\\"
data-sap-ui-language=\\"en\\"
data-sap-ui-flexibility-services='[{\\"connector\\":\\"LrepConnector\\",\\"layers\\":[],\\"url\\":\\"/sap/bc/lrep\\"},{\\"applyConnector\\":\\"custom.connectors.WorkspaceConnector\\",\\"writeConnector\\":\\"custom.connectors.WorkspaceConnector\\",\\"custom\\":true},{\\"connector\\":\\"LocalStorageConnector\\",\\"layers\\":[\\"CUSTOMER\\",\\"USER\\"]}]'
data-sap-ui-resource-roots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}'
data-sap-ui-frame-options=\\"allow\\"
data-sap-ui-xx-component-preload=\\"off\\"
data-sap-ui-on-init=\\"module:open/ux/preview/client/flp/init\\">
</script>
</head>
<!-- UI Content -->
<body class=\\"sapUiBody\\" id=\\"content\\">
</body>
</html>"
`;
exports[`FlpSandbox router test/flp.html UI5 snapshot 1`] = `
"<!DOCTYPE HTML>
<html lang=\\"en\\">
<!-- Copyright (c) 2015 SAP AG, All Rights Reserved -->
<head>
<meta http-equiv=\\"X-UA-Compatible\\" content=\\"IE=edge\\">
<meta charset=\\"UTF-8\\">
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\">
<title>Local FLP Sandbox</title>
<!-- Bootstrap the unified shell in sandbox mode for standalone usage.
The renderer is specified in the global Unified Shell configuration object \\"sap-ushell-config\\".
The fiori2 renderer will render the shell header allowing, for instance,
testing of additional application setting buttons.
The navigation target resolution service is configured in a way that the empty URL hash is
resolved to our own application.
This example uses relative path references for the SAPUI5 resources and test-resources;
it might be necessary to adapt them depending on the target runtime platform.
The sandbox platform is restricted to development or demo use cases and must NOT be used
for productive scenarios.
-->
<script type=\\"text/javascript\\">
window[\\"sap-ushell-config\\"] = {
defaultRenderer: \\"fiori2\\",
renderers: {
fiori2: {
componentData: {
config: {
search: \\"hidden\\",
enableSearch: false
}
}
}
},
applications: {\\"app-preview\\":{\\"title\\":\\"My Simple App\\",\\"description\\":\\"This is a very simple application.\\",\\"additionalInformation\\":\\"SAPUI5.Component=test.fe.v2.app\\",\\"applicationType\\":\\"URL\\",\\"url\\":\\"..\\"},\\"testfev2other-preview\\":{\\"title\\":\\"My Other App\\",\\"description\\":\\"This is a very simple application.\\",\\"additionalInformation\\":\\"SAPUI5.Component=test.fe.v2.other\\",\\"applicationType\\":\\"URL\\",\\"url\\":\\"/yet/another/app\\"}}
};
</script>
<script type=\\"text/javascript\\">
window[\\"data-open-ux-preview-basePath\\"] = \\"..\\";
</script>
<script src=\\"../test-resources/sap/ushell/bootstrap/sandbox.js\\" id=\\"sap-ushell-bootstrap\\"></script>
<!-- Bootstrap the UI5 core library. 'data-sap-ui-frameOptions=\\"allow\\"' is a NON-SECURE setting for test environments -->
<script id=\\"sap-ui-bootstrap\\"
src=\\"../resources/sap-ui-core.js\\"
data-sap-ui-libs=\\"sap.m,sap.ui.core,sap.ushell,sap.f,sap.ui.comp,sap.ui.generic.app,sap.suite.ui.generic.template\\"
data-sap-ui-async=\\"true\\"
data-sap-ui-preload=\\"async\\"
data-sap-ui-theme=\\"sap_horizon_dark\\"
data-sap-ui-compatVersion=\\"edge\\"
data-sap-ui-language=\\"en\\"
data-sap-ui-bindingSyntax=\\"complex\\"
data-sap-ui-flexibilityServices='[{\\"connector\\":\\"LrepConnector\\",\\"layers\\":[],\\"url\\":\\"/sap/bc/lrep\\"},{\\"applyConnector\\":\\"custom.connectors.WorkspaceConnector\\",\\"writeConnector\\":\\"custom.connectors.WorkspaceConnector\\",\\"custom\\":true},{\\"connector\\":\\"LocalStorageConnector\\",\\"layers\\":[\\"CUSTOMER\\",\\"USER\\"]}]'
data-sap-ui-resourceroots='{\\"open.ux.preview.client\\":\\"../preview/client\\",\\"test.fe.v2.app\\":\\"..\\",\\"test.fe.v2.other\\":\\"/yet/another/app\\"}'
data-sap-ui-frameOptions=\\"allow\\"
data-sap-ui-xx-componentPreload=\\"off\\"
data-sap-ui-oninit=\\"module:open/ux/preview/client/flp/init\\">
</script>
</head>
<!-- UI Content -->
Expand Down
24 changes: 24 additions & 0 deletions packages/preview-middleware/test/unit/base/flp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,30 @@ describe('FlpSandbox', () => {
expect(response.text).toMatchSnapshot();
});

test('test/flp.html UI5 legacy-free', async () => {
const jsonSpy = () =>
Promise.resolve({ libraries: [{ name: 'sap.ui.core', version: '1.136.0-legacy-free' }] });
fetchMock.mockResolvedValue({
json: jsonSpy,
text: jest.fn(),
ok: true
});
const response = await server.get('/test/flp.html?sap-ui-xx-viewCache=false').expect(200);
expect(response.text).toMatchSnapshot();
});

test('test/flp.html UI5 snapshot', async () => {
const jsonSpy = () =>
Promise.resolve({ libraries: [{ name: 'sap.ui.core', version: '1.136.0-SNAPSHOT' }] });
fetchMock.mockResolvedValue({
json: jsonSpy,
text: jest.fn(),
ok: true
});
const response = await server.get('/test/flp.html?sap-ui-xx-viewCache=false').expect(200);
expect(response.text).toMatchSnapshot();
});

test('test/flp.html', async () => {
const response = await server.get('/test/flp.html?sap-ui-xx-viewCache=false#app-preview').expect(200);
expect(response.text).toMatchSnapshot();
Expand Down

0 comments on commit 1d4ba46

Please sign in to comment.